How to compare the changes in a modelform with model (django) - python

I do not know how to compare if a modelform is equal to a model in django.
thank you very much
models.py
class Person(models.Model):
name = models.CharField(max_length=45)
lastname = models.CharField(max_length=45)
dni = models.BigIntegerField()
email = models.EmailField(max_length=30)
status = models.BooleanField()
departament = models.ForeignKey(Departament) #char
forms.py
class Form_Person(forms.ModelForm):
class Meta:
model = models.Person
fields = ['name', 'lastname', 'dni', 'address', 'phone', 'email', 'position', 'status', 'departament']
views.py
#auth.decorators.login_required(login_url='login')
def persons_person(request,id='id'):
page_name = 'Persons'
try:
person = models.Person.objects.get(id=id)
list_departaments = models.Departament.objects.all()
list_departaments = list_departament.exclude(name = person.departament)
if request.method == 'POST':
form_person = forms.Form_Person(request.POST, initial='person')
Here the comparison would be implemented
### code to compare ###
# if form_persona.is_valid() and form_person.has_changed(): #Something like that
# ***how to compare***
# form_person.save()
except models.Person.DoesNotExist as e:
person = None
list_departaments = None
return render(request, 'app/persons/person.html',
{'page_name':page_name,
'person':person,
'list_departaments':list_departaments})

The link in the duplicate flag suggests using save method on object (same can be done in form also). I would personally suggest using signals with pre_save option to check before saving.

Related

Django REST: ignoring custom fields which are not part of model

My TimeReport model looks like this:
class TimeReport(models.Model):
minutes_spent = models.PositiveIntegerField()
task = models.ForeignKey(Task, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)
reported_for = models.DateField()
note = models.TextField(null = True, blank=True)
status = models.CharField(max_length=50, choices=State.choices, default=State.new)
user = models.ForeignKey(User, on_delete=models.PROTECT)
And my model serializer:
class TimeReportCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TimeReport
fields = (
'id',
'minutes_spent',
'reported_for',
'note',
'status',
'task_custom_id',
)
task_custom_id = serializers.CharField()
def create(self, validated_data):
user = User.objects.get(auth_user_id = self.context['user_id'])
task = Task.objects.filter(custom_id = validated_data['task_custom_id']).filter(user = user.id).first()
report = TimeReport(**validated_data)
report.user = user
report.task = task
report.save()
return report
So, the problem is, that I want to take a custom value in a serializer, which is not a part of a model and do some custom logic with it - in this case search for the right 'task' in the database. But when I try to parse the model by using report = TimeReport(**validated_data), it gives me an exception:
TypeError at /api/report/
TimeReport() got an unexpected keyword argument 'task_custom_id'
Im kind of new to Django and python itself, so - what is the best approach?
If you are going to use that field only for creation, you should use write_only option.
task_custom_id = serializers.CharField(write_only=True)
See the docs here https://www.django-rest-framework.org/api-guide/fields/#write_only
You just need to remove task_custom_id from the dictionary
class TimeReportCreateSerializer(serializers.ModelSerializer):
class Meta:
model = TimeReport
fields = (
'id',
'minutes_spent',
'reported_for',
'note',
'status',
'task_custom_id',
)
task_custom_id = serializers.CharField()
def create(self, validated_data):
user = User.objects.get(auth_user_id = self.context['user_id'])
task_custom_id = validated_data.pop("task_custom_id")
task = Task.objects.filter(custom_id = task_custom_id).filter(user = user.id).first()
report = TimeReport(**validated_data)
report.user = user
report.task = task
report.save()
return report
task = Task.objects.filter(custom_id = validated_data.pop('task_custom_id')).filter(user = user.id).first()
the **validated_data will return (task_custom_id=value, field1=value1 ...) and task_custom_id it's not a TimeReport field so all u need is to pop it from validated_data before calling the constructor TimeReport

Django serializer, nested relation and get_or_create

I've been bugging on this issue for some time now. I have two models : Acquisitions and RawDatas.
Each RawData have one Acquisition, but many RawDatas can have the same Acquisition.
I want to create or get the instance of Acquisition automatically when I create my RawDatas. And I want to be able to have all informations using the serializer.
class Acquisitions(models.Model):
class Meta:
unique_together = (('implant', 'beg_acq', 'duration_acq'),)
id = models.AutoField(primary_key=True)
implant = models.ForeignKey("Patients", on_delete=models.CASCADE)
beg_acq = models.DateTimeField("Beggining date of the acquisition")
duration_acq = models.DurationField("Duration of the acquisition")
class RawDatas(models.Model):
class Meta:
unique_together = (('acq', 'data_type'),)
id = models.AutoField(primary_key=True)
acq = models.ForeignKey("Acquisitions", on_delete=models.CASCADE)
data_type = models.CharField(max_length=3)
sampling_freq = models.PositiveIntegerField("Sampling frequency")
bin_file = models.FileField(db_index=True, upload_to='media')
And my serializers are these :
class AcquisitionSerializer(serializers.ModelSerializer):
class Meta:
model = Acquisitions
fields = ('id', 'implant', 'beg_acq', 'duration_acq')
class RawDatasSerializer(serializers.ModelSerializer):
acq = AcquisitionSerializer()
class Meta:
model = RawDatas
fields = ('id', 'data_type', 'sampling_freq', 'bin_file', 'acq')
def create(self, validated_data):
acq_data = validated_data.pop('acq')
acq = Acquisitions.objects.get_or_create(**acq_data)
RawDatas.objects.create(acq=acq[0], **validated_data)
return rawdatas
My problem is that, using this, if my instance of Acquisitions already exists, I get a non_field_errors or another constraint validation error.
I would like to know what is the correct way to handle this please ?
So I can automatically create this using the nested serializer, and when I only want to have informations (such as a GET request), I can have all the field I need (every field of the two models).
Thanks in advance for your help !
Try this:
class AcquisitionSerializer(serializers.ModelSerializer):
class Meta:
model = Acquisitions
fields = ('id', 'implant', 'beg_acq', 'duration_acq')
class RawDatasSerializer(serializers.ModelSerializer):
class Meta:
model = RawDatas
fields = ('id', 'data_type', 'sampling_freq', 'bin_file', 'acq')
def create(self, validated_data):
acq_data = validated_data.pop('acq')
acq = Acquisitions.objects.filter(id=acq_data.get('id')).first()
if not acq:
acq = AcquisitionSerializer.create(AcquisitionSerializer(), **acq_data)
rawdata = RawDatas.objects.create(acq=acq, **validated_data)
return rawdata

Django: 'readonly' attribute doesn't work on my ModelForm

There is a 'league_type' field in my form and I want to make it readonly. I used widget.attr['readonly'] = True but it doesn't work.
The model:
class League(models.Model):
league_types = (
('league', 'League'),
('knockout', 'Knockout'),
)
....
season = models.ForeignKey(Season, related_name = "league_season")
league_type = models.CharField(max_length=10, choices = league_types, default ='league')
...
The ModelForm:
class LeagueForm(forms.ModelForm):
class Meta:
model = League
fields = ('title', 'league_type', 'season', 'status')
widgets = {'season':forms.HiddenInput(),}
view.py:
if League.objects.filter(season = season, league_type = 'knockout').count():
form = LeagueForm(initial={'season': season, 'league_type': 'league'})
form.fields['league_type'].widget.attrs['readonly'] = True
else:
form = LeagueForm(initial={'season': season})
Update:
I cannot use disabled attribute because I'm going to create a form with league_type initial value.

Django form custom field

If we have a modelForm with some fields not directly corresponding to the model, how do we have the form process them in a custom way, while saving the rest of fields as by default?
For example, we have a model for an item that supports multilingual descriptions. The models are:
class Item(models.Model):
name = models.ForeignKey(Localization)
on_sale = models.BooleanField(default=False)
class Localization(models.Model):
de = models.TextField(blank=True, null=True, verbose_name='de')
eng = models.TextField(blank=True, null=True, verbose_name='eng')
The form to add/edit an Item looks like that:
class ItemForm(forms.ModelForm):
id = forms.CharField(widget=forms.HiddenInput(), max_length=128, label='')
name_eng = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='eng')
name_de = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='de')
on_sale = forms.CharField(widget=forms.CheckboxInput(), label='on_sale', )
class Meta:
model = Item
fields = ('id', 'on_sale',)
Now what saving this form should do, is for a new Item - create Localization object with the two name fields, then create an Item object with on_sale field, linked to Localization object. For an existing Item - edit the corresponding Localization object and then on_sale field of the Item itself.
I did the task with a separate function, that processes the custom fields from the request separately, but having it all done by the form's save() method looks better. Or am I wrong?
PS I'm sorry to be asking an evidently worn question, but I simply fail to do what I want with other examples.
Update:
I actually got it working the way I wanted with the help of the hints from here. Code goes as that, please let me know if it can be optimized.
class NameForm(forms.ModelForm):
# id = forms.CharField(widget=forms.HiddenInput(), max_length=128, label='')
id = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='id', required=False)
name_eng = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='eng')
name_de = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='de')
gender = forms.CharField(widget=forms.CheckboxInput(), label='gender', required=False)
class Meta:
model = Name
fields = ('id', 'gender',)
def save(self):
instance = super(NameForm, self).save(commit=False)
obj_id = self.cleaned_data['id']
if obj_id:
instance_bd = Name.objects.get(pk=obj_id)
loc = instance_bd.name
loc.de = self.cleaned_data['name_de']
loc.eng = self.cleaned_data['name_eng']
loc.save()
instance.id = obj_id
else:
loc = Localization(de=self.cleaned_data['name_de'], eng=self.cleaned_data['name_eng'])
loc.save()
instance.name = loc
instance.save()
return instance
The view is simply
#login_required
def admin_lists(request):
if request.method == 'POST':
form = NameForm(request.POST)
if form.is_valid():
form.save()
forms = {'name': NameForm()}
return render(request, 'admin/lists.html', {'forms': forms})
In this case is better don't use the forms'save method, try this
if request.method == 'POST':
form = ItemForm(request.POST)
# check whether it's valid:
if form.is_valid():
post = request.POST
set_name_eng= post['name_eng']
set_name_de= post['name_de']
set_on_sale = post['on_sale']
#now here we create the anothers objects
a = Localization(de=set_name_de, eng=set_name_eng)
a.save()
b = Item(name=a, on_sale=sale)
b.save()
I didn't understand the last part, but I think you need to organize better your models and forms, Let me know and I'll try to help you

if form field is None don't find in model in that field

I have model with blank=True fields. In my form I have fields that are optional if in model they are blank=True. So, if in my form I want to make them empty, and then I want to filter my model objects, I don't want to use empty fields to search. Do I need to create other query in this case?
forms.py
class searchGoods(forms.Form):
region_from = forms.ModelChoiceField(required=False, queryset = Region.objects.all(), widget = forms.Select())
region_to = forms.ModelChoiceField(required=False, queryset = Region.objects.all(), widget = forms.Select())
models.py
class Add_good(models.Model):
loading_region = models.ForeignKey(Region, blank=True, related_name="loading_region", null=True)
unloading_region = models.ForeignKey(Region, blank=True, related_name="unloading_region", null=True)
views.py
if form['region_from'] == None:
if form['region_to'] == None:
data_from_db = Add_good.objects.filter(loading_country=form['country_from'],
unloading_country=form['country_to'],
loading_city=form['city_from'],
unloading_city=form['city_to'],
loading_goods_date_from__gte=form['date_from'],
loading_goods_date_to__lte=form['date_to'],
mass__gte=form["mass_from"],
mass__lte=form["mass_to"],
volume__gte=form['volume_from'],
volume__lte=form['volume_to'],
auto_current_type__in=auto_types,
)
else:
data_from_db = Add_good.objects.filter(loading_country=form['country_from'],
unloading_country=form['country_to'],
loading_city=form['city_from'],
unloading_city=form['city_to'],
unloading_region=form["region_to"],
loading_goods_date_from__gte=form['date_from'],
loading_goods_date_to__lte=form['date_to'],
mass__gte=form["mass_from"],
mass__lte=form["mass_to"],
volume__gte=form['volume_from'],
volume__lte=form['volume_to'],
auto_current_type__in=auto_types,
)
else:
if form['region_to'] == None:
data_from_db = Add_good.objects.filter(loading_country=form['country_from'],
unloading_country=form['country_to'],
loading_city=form['city_from'],
unloading_city=form['city_to'],
loading_region=form["region_from"],
loading_goods_date_from__gte=form['date_from'],
loading_goods_date_to__lte=form['date_to'],
mass__gte=form["mass_from"],
mass__lte=form["mass_to"],
volume__gte=form['volume_from'],
volume__lte=form['volume_to'],
auto_current_type__in=auto_types,
)
else:
data_from_db = Add_good.objects.filter(loading_country=form['country_from'],
unloading_country=form['country_to'],
loading_city=form['city_from'],
unloading_city=form['city_to'],
loading_region=form["region_from"],
unloading_region=form["region_to"],
loading_goods_date_from__gte=form['date_from'],
loading_goods_date_to__lte=form['date_to'],
mass__gte=form["mass_from"],
mass__lte=form["mass_to"],
volume__gte=form['volume_from'],
volume__lte=form['volume_to'],
auto_current_type__in=auto_types,
)
Well, exactly, in my models there is more fields, all of them you can see in views when I save them
The situation is that I forget to do makemigrations. After that all works fine!

Categories