automatically saved value in a UpdateView Django - django

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

Related

How to update foreignkey field manually in UpdateView post method?

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

Django: Display user's previous choices for a ModelForm in the template

I am trying to create a user profile page where users can see and update their preferences for certain things, like whether they are vegetarian, or have a particular allergy, etc. I want the data to be displayed as a form, with their current preferences already populating the form fields.
So I've created the following Model:
class FoodPreferences(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE) # One user has one set of food prefs
vegetarian = models.BooleanField()
vegan = models.BooleanField()
...
that's referenced in my forms.py:
class FoodPreferencesForm(forms.ModelForm):
class Meta:
model = FoodPreferences
exclude = ('user', )
I've tried creating a view that inherits FormView and then referencing the form, like this:
class UserProfileView(generic.FormView):
template_name = "registration/profile.html"
form_class = FoodPreferencesForm
success_url = reverse_lazy('user_profile')
This saves the form to a instance of the model correctly, but obviously it just displays the blank form again, after updating, so the user has no idea what their current preferences are.
To implement this I thought I might need to override get() and post() to get the instance of FoodPreferences for the user, and then pass those values into the form like you would a request.POST object. However, firstly, I don't know how to do that, and secondly I'd be taking responsibility for correctly updating the database, which the FormView was already doing.
This is what I've got for that solution:
def get(self, request, *args, **kwargs):
prefs = FoodPreferences.objects.get(user=request.user)
form = self.form_class(prefs)
return render(request, self.template_name, {'form': form, })
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if not form.is_valid():
return render(request, self.template_name, {'form': form, 'error': 'Something went wrong.'})
curr_prefs = FoodPreferences.objects.update_or_create(form.fields)
prefs.save()
return render(request, self.template_name, {'form': form, })
but I get a TypeError: argument of type 'FoodPreferences' is not iterable on the line in get():
form = self.form_class(prefs)
because it's not expecting a model instance.
Am I thinking about this in the right way? This seems like a common enough problem that Django would have something inbuilt to do it, but I can't find anything.
You should only rarely need to define get or post in a class-based view, and you definitely don't here.
To start with, you need to use a more appropriate base class for your view. Here you want to update an existing item, so you should use UpdateView.
Secondly, you need to tell the class how to get the existing object to update, which you can do by definining get_object. So:
class UserProfileView(generic.UpdateView):
template_name = "registration/profile.html"
form_class = FoodPreferencesForm
success_url = reverse_lazy('user_profile')
def get_object(self, queryset=None):
return self.request.user.foodpreferences
# or, if you aren't certain that the object already exists:
obj, _ = FoodPreferences.objects.get_or_create(user=self.request.user)
return obj

how to alter/change the post data in django UpdateView

I am using django update view for my model/records editing stuff like below
class EditProductView(LoginRequiredMixin, UpdateView):
model = Product
def get_template_names(self):
return ['website/product/edit_product.html']
def get_success_url(self):
return reverse('product_details', args=[self.kwargs['pk']])
def get_context_data(self, **kwargs):
publisher = Publisher.objects.get(product__id=self.kwargs['pk'])
context = super(EditProductView, self).get_context_data(**kwargs)
context.update(
{
'publisher':publisher,
}
)
return context
edit_product = EditProductView.as_view()
So what all i want/trying to do is alter(add some data, edit already submitted data according to website functionality) the POST data before submitting to form,
So i know that UpdateView has some method def def post(self, request, *args, **kwargs): , but i dont know exactly how to use it
Suppose below is the request.POST data i am getting
<QueryDict: {u'product_name': [u'Biscuit'], u'product_price': [u'1000'], u'product_tag': [u'']}>
So now i want to alter the above QueryDict and if the value of product_tag was empty i need assign some default one and submit with latest querdict
Also i know that Querydict is mutable, but because of sure i need to edit the POST data, before submitting/saving to database, i need to make that querydict as dict, then edit it, and convert back to querdict
So after all whats my question is
How can we alter the POST data in UpdateView before submitting/saving to database
Is the post method heplful?
The QueryDict is mutable after you create its .copy(). See the docs.
Update Example:
class SomeUpdateView(UpdateView):
def post(self, request, **kwargs):
request.POST = request.POST.copy()
request.POST['some_key'] = 'some_value'
return super(SomeUpdateView, self).post(request, **kwargs)
Here is much broader discussion about the topic.
Furthermore, shouldn't this be done in ModelForm subclass? You're certainly aware you can set custom form as a form_class in UpdateView. Such a logic usually needs unit tests and it's much easier to unit test logic which sits in the form.

Cannot validate dynamic choices with Django ModelForm

I have a Django ModelForm in Google App Engine with a ChoiceField, let's say location:
class MyForm(ModelForm):
location = ChoiceField(label="Location")
class Meta:
model = MyModel
In order to dynamically add the choices for location, and not have issues with app caching, I add them after the form has initialized:
form = MyForm(request.POST, instance=my_instance)
form.fields['location'].choices = Location.all().fetch(1000)
The problem I'm having now is that when the form is initialized via the data in request.POST the choices do not yet exist and I am receiving an error stating that an invalid choice is made (since the value does not yet exist in the list of choices).
I don't like that validation is occurring when I am initializing the form instead of waiting until I call form.is_valid(). Is there any way to suppress validation during my object instantiation? Or some other way to fix this?
UPDATE: I'm pretty sure ModelFormMetaclass is causing me my grief by validating the provided instance when the form is created. Still not sure how to fix though.
Thanks!
There must be other ways to do this, but possibly the most straightforward is to add the field in the form's __init__() method:
class MyForm(ModelForm):
...
def __init__(self, *args, **kwargs):
try:
dynamic_choices = kwargs.pop('dynamic_choices')
except KeyError:
dynamic_choices = None # if normal form
super(MyForm, self).__init__(*args, **kwargs)
if dynamic_choices is not None:
self.fields['location'] = ModelChoiceField(
queryset=dynamic_choices)
class Meta:
model = MyModel
And your view would look something like:
def my_view(request):
locations = Location.objects.all() # or filter(...) or whatever
dynamic_form = MyForm(dynamic_choices=locations)
return direct_to_template(request,
'some_page.html',
{'form': dynamic_form},)
Let us know how that works for you.

Newbie Django Question : Can't find data I thought I preset in a Form

I'm still getting to grips with Django and, in particular, Forms.
I created MyForm which subclasses forms.Form in which I define a field like this :
owner = forms.CharField(widget=forms.HiddenInput)
When I create a new, blank instance of the Form I want to prefill this with the creator's profile, which I'm doing like this :
form = MyForm( {'owner' : request.user.get_profile()} )
Which I imagine sets the owner field of the form to the request.user's id. (The type of the corresponding "owner" field in the models.Model class is ForeignKey of Profile.)
Before rendering the form, I need to check one piece of information about the owner. So I try to access form.owner, but there seems to be no "owner" attribute of the form object. I also tried form.instance.owner, but similarly, no luck.
What am I doing wrong? What have I misunderstood?
You can access this value via the form's data dictionary:
form.data.get('owner')
Initial data in a form should be passed in with the initial kwarg.
Once you've turned the form into a bound form (usually by passing request.POST in as the first argument to instantiate the form, the place you are currently incorrectly providing the initial dictionary), and performed validation with form.is_valid(), the data the user submitted will be in form.cleaned_data, a dictionary. If they changed the initial value, their changed value will be in that dictionary. If not, your initial value will be.
If you don't want to let the user modify the value, then don't have it be a field, instead pass it in as a kwarg, and store it as an instance attribute in form.__init__():
class MyForm(Form):
def __init__(self, *args, profile, **kwargs):
super().__init__(*args, **kwargs)
self.profile = profile
...
form = MyForm(
request.POST if request.POST else None,
profile=request.user.get_profile(),
)
if request.method == "POST" and form.is_valid():
do_stuff_with(form.profile)
Also as with most things, this all gets easier if you drink the Django kool-aid and use generic views.
class MyView(FormView):
form_class = MyForm
...
def get_form_kwargs(self):
return {
**super().get_form_kwargs(),
"profile": self.request.user.get_profile()
}
def form_valid(self, form):
do_stuff_with(form.profile)
return super().form_valid(form)
Or for the initial case whereby you want it to be editable:
class MyView(FormView):
form_class = MyForm
...
def get_initial(self):
return {
**super().get_initial(),
"profile": self.request.user.get_profile()
}
def form_valid(self, form):
do_stuff_with(form.cleaned_data.get("profile"))
return super().form_valid(form)
If MyForm happens to be a form about one single instance of a specific model then this gets even easier with UpdateView instead of FormView. The more you buy into the way Django wants to do things, the less you have to work.