Django modelform submission with relational data - python

I have a django modelform that creates a new listing in my post app, I would like to associate this with a company id, which is an account type in my account app.
The account_company db table (postgresql) has a user_id field which is the pk of User. the post_listing table will have a company field which I think should be the pk of account_company.
As I am attempting to use modelforms for all forms, I am having an issue with making this association.
# models.py
class Listing(models.Model):
title = models.CharField(max_length=200)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
...
# forms.py
class newListingForm(forms.ModelForm):
class Meta:
model = Listing
fields = ('title'...)
def __init__(self, user, *args, **kwargs):
super(newListingForm, self).__init__(*args, **kwargs)
self.fields['company'].queryset = Company.objects.filter(pk__user_id=user.id)
# above i am trying to filter the AutoField to the `company pk` where `user_id` is equal to `request.user.id`
# views.py
def post(request):
if request.method == 'POST':
form = newListingForm(request.user, request.POST)
if form.is_valid():
listing = form.save(commit=False)
listing.user = request.user
listing.save()
else:
form = newListingForm(request.user)
return render(request, 'post/post.html', {'form': form})
The debug error i get is:
Unsupported lookup 'user_id' for AutoField or join on the field not permitted.
Updated
#accounts model.py
...
class Company(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=200, null=True)
photo = models.ImageField(upload_to='images/%Y/%m/%d/', null=True)
description = models.TextField(null=True)

Related

How to set a foreignkey field in views?

I'm trying to save the customer field on the Test model, I'm not getting any errors but it's not saving the field either, how do I fix it?
Models
class Test(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, blank=True, null=True)
email = models.EmailField(max_length=200, blank=False)
Forms
class TestForm(forms.Form):
email = forms.EmailField(required=True)
class Meta:
model = Test
fields = ("email")
def save(self, commit=False):
# Creating the customer object
Test.objects.create(email=self.cleaned_data['email'])
Views
def test_view(request):
customer = request.user.customer
if form.is_valid():
email = form.cleaned_data['email']
customer = customer
form.save()
You can use cleaned_data to save the ModelForm.
forms.py
class TestForm(forms.ModelForm):
class Meta:
model = Test
fields = ["email"]
Assuming, you have request method POST.
views.py
def test_view(request):
if request.method=="POST":
form=TestForm(request.POST)
customer = request.user.customer
if form.is_valid():
email = form.cleaned_data['email']
test=Test(customer=customer,email=email)
test.save()
You need to use a ModelForm, then save the object without commiting, edit the customer of the object, then commit.
class TestForm(forms.ModelForm):
class Meta:
model = Test
fields = ["email", ]
def test_view(request):
customer = request.user.customer #I'm not sure this line is right, but I can't see all your models
if form.is_valid():
test = form.save(commit=False)
test.customer = customer
test.save()

How can I do username in reviews on django?

I'm doing reviews on django, but I want the user to not be able to enter any name. I want the username in the reviews to match the username of his profile
models.py
class Reviews(models.Model):
name = models.CharField('Имя', max_length=100)
text = models.TextField('Отзыв', max_length=3400)
parent = models.ForeignKey('self', verbose_name='Родитель', on_delete=models.SET_NULL, null=True, blank=True)
book = models.ForeignKey(BookModel, verbose_name='книга', on_delete=models.CASCADE)
name_user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
views.py
class MoreInfoView(View):
""" """
def get(self, request, id):
book_info = BookModel.objects.filter(id=id).first()
stuff = get_object_or_404(BookModel, id=self.kwargs['id'])
total_likes = stuff.total_likes()
return render(request, 'bookapp/more_info.html', context={
'id': id,
'book_info': book_info,
'book': BookModel.objects.all(),
'total_likes': total_likes,
})
class AddReview(View):
"""Add Review"""
def post(self, request, pk):
form = ReviewForm(request.POST)
book = BookModel.objects.get(id=pk)
if form.is_valid():
form = form.save(commit=False)
form.book = book
form.save()
return HttpResponseRedirect(reverse('more_info', args=[pk]))
forms
class ReviewForm(forms.ModelForm):
class Meta:
model = Reviews
fields = ("name", "text", 'name_user')
You can add user manually after validating ReviewForm
I also added some changes(suggestions)
models.py
class Reviews(models.Model):
name = models.CharField('Имя', max_length=100)
text = models.TextField('Отзыв', max_length=3400)
parent = models.ForeignKey('self', verbose_name='Родитель', on_delete=models.SET_NULL, null=True, blank=True)
book = models.ForeignKey(BookModel, verbose_name='книга', on_delete=models.CASCADE, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
Setting blank=True makes the field optional.
views.py
class MoreInfoView(View):
""" """
def get(self, request, id):
book_info = BookModel.objects.filter(id=id).first()
stuff = get_object_or_404(BookModel, id=self.kwargs['id'])
total_likes = stuff.total_likes()
return render(request, 'bookapp/more_info.html', context={
'id': id,
'book_info': book_info,
'book': BookModel.objects.all(),
'total_likes': total_likes,
})
class AddReview(View):
"""Add Review"""
def post(self, request, pk):
user = request.user
# User has to be authenticated to create a review. And backend must
# validate it. You should raise PermissionDenied as response or
# redirect user to the login page, or something similar.
if not request.user.is_authenticated:
raise PermissionDenied()
form = ReviewForm(request.POST)
book = BookModel.objects.get(id=pk)
if form.is_valid():
form = form.save(commit=False)
form.book = book
form.user = user
form.save()
return HttpResponseRedirect(reverse('more_info', args=[pk]))
forms.py
class ReviewForm(forms.ModelForm):
class Meta:
model = Reviews
fields = ("name", "text")
I would advise to work with a CreateView [Django-doc] that will simplify a lot of the logic. You can implement this as:
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView
class AddReviewView(LoginRequiredMixin, CreateView):
form_class = ReviewForm
def get_success_url(self):
return reverse('more_info', args=[self.kwargs['pk']])
def form_valid(self, form):
form.instance.book_id = self.kwargs['pk']
form.name_user = self.request.user
return super().form_valid(form)
In your ReviewForm you thus remove the name_user as fields element.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: You can limit views to a class-based view to authenticated users with the
LoginRequiredMixin mixin [Django-doc].
Note: normally a Django model is given a singular name, so Review instead of Reviews.
Note: Models normally have no …Model suffix. Therefore it might be better to rename BookModel to Book.

Django form.save() does not return a pk value, when pk is UUID

I'm running my project on Python 3.7.5, Django >=2.2.8,<3.0.0 and Postgres 12.1
I have implemented 2 models:
class CustomUser(AbstractUser):
objects = CustomUserManager()
person = models.ForeignKey(
'Person', on_delete=models.DO_NOTHING, null=True)
class Person(models.Model):
uuid = models.UUIDField(primary_key=True, db_column='person_uuid')
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
address1 = models.CharField(max_length=255, blank=True, null=True)
address2 = models.CharField(max_length=255, blank=True, null=True)
add_date = models.DateTimeField(auto_now_add=True)
mod_date = models.DateTimeField(auto_now=True)
The CreateUser view shown below is for registering new users. This view consists of 2 forms, a user creation form and a person form. Once the user enters their details I want to save the person_form and set the person field in CustomUser to the newly created person. But the value of pk after save is always None.
To work around this, I query the Person model with the matching first_name and last_name and use the UUID from the response. But this is very brittle, as I have other models that use UUID and don't have unique values like first_name and last_name. I wonder if there is a more reliable way of getting the UUID of the form just saved.
class CreateUserView(View):
template_name = 'core/create_user.html'
def get(self, request, *args, **kwargs):
user_form = CustomUserCreationForm()
person_form = PersonForm()
context = {'person_form': person_form,
'user_form': user_form}
return render(request, self.template_name, context=context)
def post(self, request, *args, **kwargs):
person_form = PersonForm(request.POST)
user_form = CustomUserCreationForm(request.POST)
print('Person form is valid: {}'.format(person_form.is_valid()))
if person_form.is_valid() and user_form.is_valid():
print('User form is valid')
person = person_form.save()
print(f'PK of new person is: {person.pk}')
p = Person.objects.filter(first_name=person.first_name,
last_name=person.last_name).latest('add_date')
user = user_form.save(commit=False)
user.person = p
user.save()
messages.success(request, 'Account created successfully')
return redirect('login')
else:
return render(request, self.template_name, {'person_form': person_form,
'user_form': user_form})
Note: I realize that Person and CustomUser can be a single model, but Person is used elsewhere in the database with other entities and needs to be independent of user
I know this is late, but you might find this interesting, and also this. It seems you don't get any return because your primary key is no AutoField.

how to make django default user when using formsets

I have this model that i want to make the create_by default for current user
class order(models.Model):
name = models.CharField(max_length=300, null=True,)
description = models.CharField(max_length=300, null=True,blank=True)
date_created = models.DateTimeField(auto_now_add=True, null=True)
created_by = models.CharField(max_length=200,default=None,null=True,blank=True)
in Django models I cant use request.user
I tried in views.py but i cant do it since i am using modelformsets
I tried this
def create_order(request):
orderformset = modelformset_factory(order, form=orderForm)
queryset = order.objects.none()
user = request.user
formset = orderformset(request.POST or None,queryset=queryset)
if formset.is_valid():
created=formset.save(commit=False)
created.User = request.user
created.save()
return redirect('home')
How i can give default user to this orders using modelformsets
In Your models file, use foreign key for user as follows
from django.contrib.auth import get_user_model
class order(models.Model):
name = models.CharField(max_length=300, null=True,)
description = models.CharField(max_length=300, null=True,blank=True)
date_created = models.DateTimeField(auto_now_add=True, null=True)
created_by = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
Then in your views wherever you handle GET request, intialize the form as such
def create_order(request, *args, **kwargs):
if request.method == 'GET':
initial = {'created_by':request.user}
form = orderform(initial=initial)
context['form'] = form
...
Then When you're handling the POST Request, just pass the POST and save it
def create_order(request, *args, **kwargs):
if request.method == 'POST':
form = orderform(request.POST)
if form.is_valid():
form.save()
...
This is How I'd handle This Scenario in django, Try out Class Based View. Its very good once you learn its abstract methods.
models.py
from django.contrib.auth import get_user_model
class Order(models.Model):
name = models.CharField(max_length=300, null=True,)
description = models.CharField(max_length=300, null=True,blank=True)
date_created = models.DateTimeField(auto_now_add=True, null=True)
created_by = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
forms.py
I Use hidden input to hide the input field, You can leave it open if you want. it will appear as a ChoiceField
from django import forms
from .models import Order
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = '__all__'
widgets = {
'created_by': forms.HiddenInput()
}
views.py
I use the Create view and override get initial method to pass user as initial value
from django.views.generic import CreateView
from .models import Order
class OrderCreateView(CreateView):
model = Order
form_class = OrderForm
template_name = 'order_create.html'
def get_initial(self):
initial = super().get_initial()
initial['created_by'] = self.request.user
return initial
order_create.html
...
<form method='POST'>
{{form}}
<button type="submit">Submit</button>
</form>
...

excluding id field in an inline formset when saving

I have two models, connected by a foreign key. One is an inline formset. For some reason, the formset ids are being identified as Primary Keys in the database, and every time the form is submitted, the table belonging to the formset is basically overwritten. When saving, how do I ignore the formset ids
models.py
class Student(models.Model):
first_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=40)
email = models.EmailField()
class Courses(models.Model):
student = models.ForeignKey(Student)
course_name = models.CharField(max_length=40)
start_time = models.TimeField()
forms.py
class CoursesForm(forms.ModelForm):
class Meta:
model = Courses
exclude = ("student",)
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = "__all__"
views.py
...
def post(self, request, *args, **kwargs):
sform = StudentForm(request.POST, instance=Student())
CourseSchedule = inlineformset_factory(Student, Courses, form=CoursesForm, can_delete=False, extra=0, min_num=1)
cforms = CourseSchedule(data=request.POST)
if sform.is_valid() and cforms.is_valid():
sform_obj = sform.save()
for cform in cforms.forms:
cform_obj = cform.save(commit=False)
cform_obj.student = sform_obj
cform_obj.save()
....
I figured it out.. My formset data was persisting after submission. I had to declare an empty queryset in the basemodelformset.

Categories