What is the best procedure in Django for saving a form related to another model without the use of inline formsets?
Problem setup:
Model Address is related by a foreign key to Model User
Each User can have multiple Addresses. I want to add a new address to an User.
views.py
In the AddAddress class (extending CreateView) the form.errors has the error
{'user': ['This field is required.']}
The user pk is in the url /address/add/<int:pk>
First, as Daniel Roseman noted, have to make sure the field "user" does not exist in the fields list of the form. This will make sure that the form is valid.
Override the form_valid method in the view class to save the form without commiting, then setting the required user to the resulting instance and then invoking the save directly on the it.
def form_valid(self, form):
address_obj = form.save(False)
address_obj.user = User.objects.get(pk=self.kwargs['pk'])
return HttpResponseRedirect(self.get_success_url())
Related
I am adding a new field named "user" to the "order" model. I did make migrations.
(Bonus question is: why in the sql3db column "user_id" was made, instead of "user"? But ok, I change form.fields['user'] to form.fields['user_id'], as the machine wants...)
I remove unneeded fields from the form in forms.py, and I try to add this fields in views.py (because I don't know how to send "user" to the forms.py, and I think it more secure like this).
And as I can understand - my new fields is absent for "order", when I use form.save().
This field can't be null by design.
Your form has no user field, hence that will not work. What you can do is alter the object wrapped in the form with:
if form.is_valid():
form.instance.user = request.user
form.instance.email = request.user.email
order = form.save()
I opened up a prior issue on SO regarding manytomany fields not being saved in Django Createview here. Django CreateView With ManyToManyField After troubleshooting most of today, I have found that this code actually works:
class CreateAuthorView(LoginRequiredMixin,CreateView):
def form_valid(self, form):
instance = form.save(commit=False)
instance.save()
instance = form.save()
if instance.access_level == "Custom":
obj = NewAuthor.objects.get(secret=instance.name)
obj.access.add(instance.created_by.id)
print(instance.created_by.id)
print(obj.access.all())
instance = form.save()
obj.save()
form.save_m2m()
instance = form.save()
return super(CreateAuthorView, self).form_valid(form)
When I issue the print(obj.access.all()) I can see in my console that the
obj.access.add(instance.created_by.id)
Line of code actually does exactly what I want it to do...it adds the created_by.id to the access(ManyToManyField) field that I have defined in my model. However, when the record actually gets cut, only the values that the user selected in the form are added to the access field, and the created_by.id never makes it to the database.
Should I be overriding CreateView somewhere else in order for the created_by to take affect? It appears as if my initial update in form_valid is being overwritten is what I suspect. Actually I've proven it because my update is in fact in my console but not making it to the database. Thanks in advance for any thoughts on how best to solve.
I found the answer to my question via this SO question. It turns out you have to override SAVE in the ModelForm in order to save M2M fields. Save Many-To-Many Field Django Forms
This one was tricky.
I have a form with some fields. I want to display all fields in form except two. However one field data needs to be displayed.
I am able to get the form but not able to retrieve the data from DB to display.
Model.py
class Company(models.Model):
STATUS_CHOICES=(
('service','service'),
('product','product'),
)
user=models.OneToOneField(settings.AUTH_USER_MODEL)
company_name=models.CharField(max_length=250)
company_address=models.CharField(max_length=250)
Company_telephone=models.CharField(max_length=250,blank=True)
company_email=models.CharField(max_length=250,blank=True)
company_website=models.CharField(max_length=250,blank=True)
VAT=models.CharField(max_length=250,blank=True)
Service_Tax=models.CharField(max_length=250,blank=True)
company_PAN=models.CharField(max_length=250,blank=True)
company_bankdetails=models.CharField(max_length=250,blank=True)
invoice_type=models.CharField(max_length=250,choices=STATUS_CHOICES,default='service')
def __str__(self):
return 'self.user.company_name'
forms.py
class companyeditform(forms.ModelForm):
class Meta:
model=Company
exclude = ('user','company_name',)
views.py
#login_required
def companyadd(request):
if request.method == 'POST':
company_form=companyeditform(instance=request.user.company,data=request.POST)
if company_form.is_valid():
new_form=company_form.save(commit=False)
new_form.save()
return render(request,'account/dashboard.html',{'section':'addcompany'})
else:
company_form=companyeditform(instance=request.user.company)
company_details=Company.objects.get(user=request.user.company)
return render(request,'account/company.html',{'company_form':company_form})
When form is displayed everything works as planned. However not getting company_name.
Using this query to get company name.
company_details=Company.objects.get(user=request.user.company)
Django gives following error:
Cannot query "self.user.company_name": Must be "User" instance.
In this query company_details=Company.objects.get(user=request.user.company) you are trying to get the company of a particular user. But in the statement, you are comparing user=request.user.company, both are two different types (User is the authuser model and request.user.company is the Company model). You cannot do this in the query.
company_details=Company.objects.get(user=request.user) This statement will solve the issue. And also you can do
company_details=request.user.company because the association is OneToOne.
The reason you are getting that error, is because you are trying to fetch a Company by filtering it out the company that matches the current user, but you are passing in the actual company object:
company_details=Company.objects.get(user=request.user.company)
# ^^^^^^^
You can fix the line, by doing this:
company_details=Company.objects.get(user=request.user)
But you already have the correct object, in request.user.company, you don't need to fetch it again, simply:
company_details = request.user.company
print(company_details.company_name)
In fact, since you are using the render shortcut, you don't even need to do this step, as the request object will be available in your template, so all you need to do really is:
Company: {{ request.user.company.company_name }}
Finally, you should always redirect after a POST request (see this article on wikipedia for the details).
request.user might be a class like AnonymousUser. Do some extra processing to ensure that request.user is the type provided by django.contrib.auth.get_user_model().
Please this as a consideration question. Maybe somebody will use one of the
solutions below.
I have a couple of models which contain a ForeignKey(User) field.
My class-based create views are derived from the generic CreateView.
There are two options to save the associated user when adding a new object:
Saving the form in the views by overriding the form_valid method;
this doesn't expose user_id (and other not mentioned here data that should not be exposed)
class CreateOfferView(CreateView):
model = Offer
form_class = SomeModelFormWithUserFieldExcluded
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
instance.save()
Saving the form with user id stored (and exposed) in a hidden field.
Here's the tricky part. There are more models with user field... so
when creating a form I need to fill the user field with initial (currently logged in) user and also I need to make that field hidden. For this purpose I've used my OwnFormMixin
class OwnFormMixin(object):
def get_form(self, form_class):
form = super(OwnFormMixin, self).get_form(form_class)
form.fields['user'].widget = forms.HiddenInput()
def get_initial(self):
initial = super(OwnFormMixin, self).get_initial()
initial['user'] = self.request.user.pk
#I could also do this in get_form() with form.fields['user'].initial
class CreateOfferView(OwnFormMixin, CreateView):
model = Offer
form_class = SomeModelFormWithAllFields
There are more CreateXXXView using the OwnFormMixin..
How do you save your user data in the forms?
Hidden vs. saving directly in your views? What are pros/cons?
Unless you're allowing users to modify that ForeignKeyField, there's no reason to include it in a form — I'd go with your first solution of using exclude to keep the user field out of your ModelForm, and setting the user from request.user. In fact, the Django documentation now has an example along these exact lines.
You have the advantage of not having to secure against manipulation of the user_id parameter, not exposing your internal user IDs and not having to worry about the different Create vs. Update cases. A slight disadvantage is that if you ever need the ability to change an object's associated User you'll need to start again.
I have a form that I use to display several fields from a record to the user. However, the user should not be able to update all the fields that are displayed. How do I enforce this? It would nice if I could specify which fields to save when calling form.save, but I couldn't get this to work. Here's some of the code:
obj = get_object_or_404(Record, pk=record_id)
if request.method == 'POST':
form = forms.RecordForm(request.POST, instance=obj)
if form.is_valid():
form.save()
I don't think using exclude or fields in the form's Meta definition will work as this will only display the fields the user is allowed to update.
You can override the form's save() method:
class MyModelForm(forms.ModelForm):
def save(self, commit=True):
if self.instance.pk is None:
fail_message = 'created'
else:
fail_message = 'changed'
exclude = ['field_a', 'field_b'] #fields to exclude from saving
return save_instance(self, self.instance, self._meta.fields,
fail_message, commit, construct=False,
exclude=exclude)
Option 1: exclude those fields, and use your template to display the data that should not be changed completely outside of the form itself. It sounds to me like they're not really part of the form, if the user can't change them.
Option 2: In a Django form, how do I make a field readonly (or disabled) so that it cannot be edited?
take this answer to mark your fields as read only... but understand there's no server side security here, so you would want to do something like getting the target model before you update it, and update those offending form fields to the existing data, before you save the form.