I'm working in a Create view and I'm trying to overwrite the form_valid method. My goal is to create a record in another table before creating in the current model.
For instance, my form has a field called name. My question is how can I retrieve this fields value within the form_valid method. A piece of code to help you understand my question:
class DeliveryCreateView(CreateView):
model = Delivery
form_class = DeliveryForm
def form_valid(self, form):
response = super(DeliveryCreateView, self).form_valid(form)
name = form["Name"] # I know this is wrong. That's the part I don't know how to do.
self.object.permissions.add(self.request.user)
self.object.save()
return response
Thanks for any help
I just realized I can use the current object, like this:
self.object.name
This gives me the name, this object if filled with all the information.
Related
Using a generic CreateView in Django I'm trying to save only the fields that have been changed by user.
I'm trying to do this in my view:
def form_valid(self, form):
if form.has_changed():
for field in form:
if field.name in form.changed_data:
continue
else:
form.instance.field=None
In the last line I got this error:
object has no attribute 'field'
Is there a way to dynamically access to each field in the form? so I could change it?
Is there a way to dynamically access to each field in the form.
Yes.
form.fields[field_name]
So I could change it?
Yes.
form.fields[field_name] = django.forms.Charfield(required=False)
But in your code, probably you want to achieve value of the field:
field_value = form[field_name].value
If you have a data bounded field, you can change also data of form:
form.data(form.add_prefix(field_name)) = new_value
But this is already wrong approach to change something here.
I can imagine: in your case you don't understand how form / modelform saves the data and therefore you want to do something unnecessary:
Once more time, how ModelForm "saves" the data in DB:
Form validate the data.
Form clean the data.
on form.save(**kwargs) - Form create the instance and:
call instance.save() if form.save(commit=True)
dont call instance.save() if form.save(commit=False)
Instance saves the data.
Form don't save anything itself, instance.save - There is all what you need.
instance.save(self, force_insert=False, force_update=False, using=None, update_fields=None)
In your case you use CreateView - there all fields should be initiated, you can not UPDATE something, this something not exists yet. You can only avoid empty values, if you want.
def form_valid(self, form):
if form.has_changed():
form.cleaned_data = {key:val for key,val in form.cleaned_data.items() if val and key in form.changed_data}
return super().form_valid(form)
al last: form.instance.field is ridiculous
form has fields. You can achieve every field by name.
Model class has fields. You can achieve every field by name.
instance has attributes or properties. You can achieve every attribute by name.
And right now: what you are really want to do?
I am trying to manually change a foreign key field (Supplier) of a model (Expenditure). I override the UpdateView post method of Expenditure and handle forms for other models in this method too. A new SupplierForm is also rendered in this view and I am tracking if this form is changed via has_changed() method of the form. If this form has changed, what I ask is overriding the related_supplier field of ExpenditureForm and picking newly created Supplier by this statement:
if supplier_form_changed:
new_supplier = related_supplier_form.save(commit=False)
new_supplier.save()
....
# This statement seems to have no effect
self.object.related_supplier = new_supplier
I override the post method with super(), so even though I explicitly state save() method for all related forms, however I don't call the save method of main model (Expenditure) since it is already handled after super(). This is what start and end of my method looks like;
def post(self, request, *args, **kwargs):
context = request.POST
related_receipt_form = self.receipt_form_class(context, request.FILES)
related_supplier_form = self.supplier_form_class(context, request.FILES)
self.object = self.get_object()
related_receipt = self.object.receipt
related_supplier_form = self.supplier_form_class(context)
expenditure_form = self.form_class(context)
inlines = self.construct_inlines()
....
return super().post(self, request, *args, **kwargs)
You may find the full code of my entire view here:
https://paste.ubuntu.com/p/ZtCfMHSBZN/
So my problem is self.object.related_supplier = new_supplier statement does not have any effect. After the update, old related_supplier object is still there, new one is saved but not attached to the updated Expenditure. Strange thing is I am doing a similar thing in the same view (also in CreateView) with receipt and no problem whatsoever.
I debugged the code via PyCharm, before the execution of super(), I can confirm that self.object.related_supplier is the newly created one, but when the super() executed, it returns back to the original supplier object.
you can override the form valid method to add things manually, an example shown below
def form_valid(self, form):
related_supplier_form.instance.related_supplier = new_supplier
valid_data = super(UpdateView, self).form_valid(form)
return valid_data
i'm new in Django and i'm learning about the views and the methods and how they work, especially with this problem. The thing is that I would like to know how to automatically save a value of a field in my model after updating an object in a UpdateView, for example when I update an object, in this case a report where I can assign a person to do it, I would like to save a model value that shows the "status" and save the value of "assigned" or something like that, to know if the report was already assigned or not. I know there are methods and that maybe one of them could be done by overwriting the class, but I do not know how to apply it or which one to use.
For help this is a simple class of a UpdateViews that i'm using:
class reporteupdate(UpdateView):
model = reporte_fallo
form_class = ReporteAsignar
template_name = 'formulario/jefe_asignar.html'
success_url = reverse_lazy('formulario:reporte_listar_jefe')
and the field of the model that I would like to assign a value to is called status.
i'm waiting for your help, since I'm stuck with that doubt. Thanks!!!
the query dict will be changable after you create a copy of it in post method so you can do this:-
class SomeUpdateView(UpdateView):
model=your model
form_class=you form
def post(self, request, **kwargs):
request.POST = request.POST.copy()
request.POST['status'] = 'Assigned'
return super(SomeUpdateView, self).post(request, **kwargs)
You could perhaps set the status flag after the form has been successfully validated, by overriding the form_valid() method in your reporteupdate view:
class reporteupdate(UpdateView):
...
def form_valid(self, form):
# Call super() to save the model and return the success url
resp = super().form_valid(form)
# Set your status flag
self.object.status = 'assigned'
self.object.save()
return resp
I am trying to get a custom UpdateView to work in Python/Django. I believe that the code that I've writtten is mostly correct, as it seems to be returning the proper Primary Key ID in the URL when I click on the associated dropdown. The problem is that I am not seeing any of the data associated with this record on the screen in update mode. The screen appears in edit mode, but there is no data. I suspect the problem is perhaps the django template in the html form? However, I have played with the form and used {{ form }} and it too returns a blank form. I've played with this all afternoon and I'm out of guesses. Here is my view:
def updating_document(request, pk):
doc = get_object_or_404(Doc, pk=pk)
form = Update_Doc_Form(request.user, request.POST)
if request.method == 'GET':
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('App:main_procedure_menu'))
else:
print("Form is invalid!")
return render(request,'Doc/update_doc.html',{'form':form })
I also have an associated form...
Form.py
class Update_Doc_Form(forms.ModelForm):
class Meta:
model = Doc
exclude = ['user']
doc_name = forms.CharField(widget=forms.TextInput)
description = forms.CharField(required=True,widget=forms.Textarea)
team = forms.CharField(widget=forms.Select)
document = forms.CharField(required=True,widget=forms.Textarea)
def __init__(self, *args, **kwargs):
super(Update_Doc_Form, self).__init__(*args, **kwargs)
self.fields['doc_name'].widget.attrs['class'] = 'name'
self.fields['description'].widget.attrs['class'] = 'description'
self.fields['team'].widget.attrs['class'] = 'choices'
self.fields['team'].empty_label = ''
I'm a newbie, but I do want to use a custom UpdateView so that I can alter some of the fields and pass user information. I feel like the code is close, just need to figure out why it's not actually populating the form with data. Thanks in advance for your help!
What a difference a day makes. Found an answer on SO this morning. Not sure how to credit the person or issue number....
The answer was to add the following line of code to my form:
user = kwargs.pop('object_user')
I also needed to add the following function to my View:
def get_form_kwargs(self):
kwargs = super(ViewName,self).get_form_kwargs()
kwargs.update({'object_user':self.request.user})
return kwargs
This question was answered originally in 2013 by Ivan ViraByan. Thanks Ivan!
I ultimately went with a standard class based UpdateView and scrapped my plans for the custom UpdateView once I was able to figure out how to use the Class Based View(UpdateView) and "pop" off the user information when passing it to the form based on Ivan ViraByan's answer in 2013.
The code above allows you to get the user but not pass it to the ModelForm so that you don't get the unexpected user error.
This may be a dumb question, but I'm a bit unsure if it's safe to manually set the cleaned_data. The docs says:
Once is_valid() returns True, you can process the form submission
safe in the knowledge that it conforms to the validation rules defined
by your form. While you could access request.POST directly at this
point, it is better to access form.cleaned_data. This data has not
only been validated but will also be converted in to the relevant
Python types for you.
For more context, say we have a modelform which has several fields such as a book's title, book's author, and a field which asks for a url.
The form conditions are: if the url field is empty, the user must provide the title and author. If the url field is given and nothing else, I would parse the html from the given url and extract the title and author automatically for the user.
In the case where I automatically grab the title and author from the url, what would be the best way to handle saving this data to the model, since the form would return an empty cleaned_data for author and title? I made sure the data parsed will conform to the validate rules I have in the model, but setting cleaned_data like this seems suspicious.
In modelform class:
def save(self, commit = True, *args, **kwargs):
parsed_title = ... # String returned by my html parsing function
parsed_author = ... # String returned by my html parsing function
self.cleaned_data['title'] = parsed_title
self.cleaned_data['author'] = parsed_author
EDIT:
Thanks, I made it like so:
def save(self, commit=True, *args, **kwargs):
instance = super(BookInfoForm, self).save(commit=commit, *args, **kwargs)
....
instance.title = parsed_title
instance.author = parsed_author
return instance
This is a bit off topic since you've already answered the original question, but the above code breaks some other part. Instead of saving the compiled info to http://..../media/books/<id> where <id> is the book id, it saves it to http://..../media/books/None.
I have a add/edit function in my views.py that handles adding and editing:
def insert_or_modify(request, id=None):
if id is not None:
book = BookModel.objects.get(pk=id)
else:
book = BookModel()
if request.method == 'POST':
form = BookInfoForm(request.POST, instance=book)
if form.is_valid():
form.save()
....
return render_to_response(...)
Is there a way to make sure the id is present so that I won't get id=None? I guess more specifically, in the save() in the modelform, is there a way to create a new instance with an id if instance.id = None? Although I thought calling super(ModelForm, self).save(...) would do that for me?
Thanks again!
In the case you present, your intention isn't setting the cleaned_data, but the model data. Therefore, instead of setting cleaned_data in the save method, just set the attributes of self.instance and then save it.
About setting cleaned_data manually, I don't think it's necessarily wrong, it may make sense to do it in the form's clean method for some cross-field validation, although it's not a common case.