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?
CodePudding user response:
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.changed_data = {key:val for key,val in form.changed_data.items() if val} return super().form_valid(form)
al last: form.instance.field
is ridiculous
form
has fields. You can achieve everyfield
by name.Model class
has fields. You can achieve everyfield
by name.instance
has attributes or properties. You can achieve everyattribute
by name.
And right now: what you are really want to do?
CodePudding user response:
What I'm trying to do is to update objects in a Model, but not directly in one step. What I need is that when a user wants to update an object, he can suggest some changes but before updating the object other user should approve his suggestions before saving changes.
As the model has many fields and changes could affect only come fields I want to modify only the edited fields.
I guess this is not the best approach, but this is what I did:
I created a second model to save change suggestions, so I have two models looking almost the same:
class MainModel(models.Model):
name= models.CharField(max_length=100)
field_1= models.CharField(null=True,blank=True, max_length=200)
field_2= models.CharField(max_length=100)
field_3= models.CharField(max_length=100)
...
field_n = bla bla bla
class SuggestionsModel(models.Model):
name= models.Foreingkey(MainModel, on_delete=models.CASCADE)
field_1= models.CharField(null=True,blank=True, max_length=200)
field_2= models.CharField(null=True,blank=True, max_length=200)
field_3= models.CharField(null=True,blank=True, max_length=200)
...
field_n = bla bla bla
suggested_by = models.Foreingkey(User, on_delete=models.CASCADE)
approved= models.BooleanField(default=False)
checked= models.BooleanField(default=False)
and in views what I did is get the 'approved' field in the form, if the user selected "True" I access every field in MainModel and check if the suggestion contains any changes, if it does, I change it in MainModel. Finally I Update the SuggestionsModel.
I'm still working on it. What I have at the moment is something like this in the UpdateView:
def form_valid(self,form):
clean=form.clean()
object_to_update=clean.get('name')
approved= clean.get('approved')
if approved==False:
#I update the suggestion as checked and that's it.
else:
#the suggestion was approved, so changes should be updated in MainModel:
fields=MainModel._meta.fields #I get a list of fields in MainModel
for field in fields:
changed = clean.get(field.name) # I check if the field named campo.name has being changed or not
if changed == None:
continue
else:
#code to update the MainModel with user's approved suggestions
# I create a dictionary with all the changes before updating so I hit the db only once.
#after all this, I can say form.is_valid and update the suggestion as approved.
As i told before, there are probably better ways of doing this, but, this works.
Thanks