Django update single field in model entry with request - django

I try to update a model entry with a POST request.
<QueryDict: {u'is_locked': [u'False']}>
I tried with a model form :
modelEntry = model.get(pk=pk)
modelForm (request.POST, initial= modelEntry)
if modelForm.is_valid():
modelForm.save()
This was not valid (csrf_exempt)..
And also tried without modelform, directly into the model :
model.objects.filter(pk=pk).update(**request.POST)
Nothing happen ..
Ideally I search a solution with modelform (to check and clean my datas before saving).
There is a proper way for this ?
Cheers

You should pass instance, instead of initial to your model form so that it knows which object to update.
modelForm (request.POST, instance = modelEntry)
if modelForm.is_valid():
modelForm.save()

Related

Django database inputs not being validated

I have this class:
class Object(models.Model):
value=models.IntegerFiled(validators=[MaxValueValidator(100)])
I get user input for the value attribute, create an object according to the user input and save it to the database. The problem is that the user can enter e.g. 120 in the form that is used to get the input from the template/html-page to the view's method. Then an invalid object is saved to the database.
How exactly does the MaxValueValidator work? When does it do anything? What purpose do validators serve?( I really couldn't find any answer to my questions in the documentation)
I do check if the input form is valid in the view, but this doesn't seem to prevent saving invalid objects by just changing the HTML attributes in the form via developer tools in the browser
You should use a ModelForm to generate your form from the model if you want your validators to be run automatically. As per docs:
Note that validators will not be run automatically when you save a model, but if you are using a ModelForm, it will run your validators on any fields that are included in your form.
Validators work with Forms
You can make a form, such as
class ObjectForm(forms.Form):
value = forms.IntegerField(validators=[MaxValueValidator(100)])
Then validate the form based on user input
if ObjectForm(request.POST).is_valid():
# save model object here
Hope this helps.

Django : How to retrieve an extra-field defined in admin for a model in a pre_save signal?

I've overloaded admin form for a model by adding an extra-field
class MyModelAdminForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput(), required=False)
class Meta:
model = MyModel
The password field isn't exists in model and I don't want it to be stored automatically.
I want to retrieve the value of this form field in the pre_save method :
#receiver(pre_save, sender=Member)
def my_pre_save_method(sender, **kwargs):
...
Actually I don't find a way to retrieve it.
Is this possible ? And How ?
Thanks
I don't full understand what you're asking here.
Anyhow, your question is far too general to answer in full. As an overview you only 'pass' clean form data in a view i.e. when you create an instance of that object. In your case this would be 'Member'.
I would suggest you start learning Django with the tutorials. They really do help, honest.
pre_save is called from model's save and it works on form level. The field password is not a model field so this will not be available on the instance of model and hence you can't access it in pre_save.
So, you can only retrieve is in the view using cleaned_data, and then use it in some way you want.

Django formset validation: automatically fix form validation errors

In an my model, I've the following
--- models.py ---
class A(models.Model):
my_Bs = models.ManyToManyField('B', through='AlinksB')
...
class B(models.Model):
...
class AlinksB(models.Model):
my_A = models.ForeignKey(A)
my_B = models.models.ForeignKey(B)
order = models.IntegerField()
So is the corresponding admin (A admin view has an inline to link B instances, and I prepared the required to custom this inline's formset and forms):
--- admin.py ---
class AlinksBInlineForm(forms.ModelForm):
class Meta:
model = AlinksB
class AlinksBInlineFormset(forms.models.BaseInlineFormSet): # there also is a BaseModelFormset
form = AlinksBInlineForm
class AlinksBInline(admin.TabularInline):
formset = AlinksBInlineFormset
model = AlinksB
class AAdmin(admin.ModelAdmin):
form = AForm
inlines = (AlinksBInline,)
...
class BAdmin(admin.ModelAdmin):
...
Now to custom the forms validation, nothing difficult: just override the "clean" method of the form object. If you want many different forms in the formset, I think you just have to change some manually in the "init" method of the formset. But what about programatically validating all the forms when we clean the formset, and that only under some conditions.
In my case: how to automatically set the "order" field (in the inline of A admin view) with an autoincrement if all the orders (inline rows to remove excluded) are empty ?!
I just spent a lot of time Googling about trying to perform automatic form cleaning during a formset validation in Django Framework. After a few days a couldn't figure a solution so I started looking right into Django's source code to see how work fields, widgets, forms and formsets.
Here is what I understood:
-All the data POSTed by the user when he submits the formset it stored in the "data" attribute of the formset. This attribute is very ugly and cannot be directly used.
- The form is just a wrapper for fields (it calls all the fields' clean methods and fill error buffers, and only a few more)
-The form fields have a widget. This widget allow getting back the field's raw value from the "data" attribute of the formset
form.add_prefix('field name') # returns the 'field prefix', the key of formset.data used to retrieve the field's raw value
form.fields['field name'].widget.value_from_datadict(form.data, form.files, 'field prefix') # returns the raw value
-The form fields also have a method to transform the raw value into a right python value (in my case: order is an integer, or None if the field has been left empty)
form.fields['field name'].to_python(raw_value) # returns a value with the right type
-You can change the value of one of the fields from the formset with the following code
form.data.__setitem__('field prefix', value) # code to update an iterable knowing the key to change
-Once you have modified the fields value, you can call the "full_clean" method of the forms to retry cleaning them (this will remove the previous errors).
-Once you have validated again the forms, you can retry validating the formset with its "full_clean" method too. But take care to avoid infinite loops
-The forms clean data can only be used has a read-only data, to add more error messages in the form or the formset
An other solution would be to manually change the "form.clean_data" attribute, and clean the formset.errors and all the form.errors
Hope it could help somebody in the same situation as me !
Ricola3D

How to save M2M Field in a formset when commit=False

deals_formset_factory = modelformset_factory(Deal, form=DealCForm, extra=1)
attached_deals_formset = deals_formset_factory(request.POST, prefix='deals')
Since some fields of my Deal model are not shown in the form and hence can't be set by the user (but the M2M field is shown and can be set by the user), I can't just do a
for fm in attached_deals_formset:
if fm.has_changed():
fm.save()
since it would break.
So theoretically the idea in such situations is to do
deal = fm.save(commit=False)
...
deal.save()
but this doesn't save my M2M field inside deal. The Through table remains untouched. What is the best approach to solve this?
class Deal(models.Model):
deal_id = UUIDField()
....
sales_item = models.ManyToManyField(SalesItem)
I found the solution, there is no need to override the save method.
Another side effect of using commit=False is seen when your model has
a many-to-many relation with another model. If your model has a
many-to-many relation and you specify commit=False when you save a
form, Django cannot immediately save the form data for the
many-to-many relation. This is because it isn't possible to save
many-to-many data for an instance until the instance exists in the
database.
To work around this problem, every time you save a form using
commit=False, Django adds a save_m2m() method to your ModelForm
subclass. After you've manually saved the instance produced by the
form, you can invoke save_m2m() to save the many-to-many form data
Source
After deal.save() simply:
fm.save_m2m()

Django "Duplicate" ModelForm

I'm wondering if there is a simple way of creating a "duplicate" ModelForm in Django - i.e. a form that is prefilled with the content of an existing model instance (excepting certain fields, such as those that are unique), but creates a new instance when saved.
I was thinking along the lines of supplying an instance to a ModelForm so that the data is prefilled as with an "edit" form, then setting the instance to None before saving, but this gives a "'NoneType' object has no attribute 'pk'" error when calling .save() on the form. It seems the act of supplying an instance when constructing the form creates some dependency on it being there at the end.
I have had trouble finding a solution to this problem, but I can't imagine a "duplicate" form being too unique, so maybe I am missing something simple?
Any help would be appreciated.
I think what you need is a way to fill in the initial values for the fields in the form. The best way to accomplish this would be to create a dictionary of initial values (keyed by field name) from an existing instance and supply this to the form.
Something like this:
class AddressForm(forms.ModelForm):
class Meta:
model = Address
# Inside view:
address = Address.object.get(**conditions)
initial = dict()
for field in ('state', 'zipcode'): # Assuming these are the fields you want to pre-fill
initial[field] = getattr(address, field)
form = AddressForm(initial = initial)
class AddressForm(forms.ModelForm):
class Meta:
model = Address
# Inside view:
address = Address.object.get(pk=<your-id>)
address.pk = None # that's the trick, after form save new object will be created
form = AddressForm(instance=address)