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
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
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
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.
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
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!