django modelforms and fields - django

I have a model with some fields and a User as a ForeignKey
class Customer(models.Model):
#fields
salesman = models.ForeignKey(User, blank=True, null=True)
and a model form
class CustomerForm(ModelForm):
class Meta:
model = Customer
I want my form to validate if salesman is also entered but not on Database level. If I add the salesman field
class CustomerForm(ModelForm):
salesman = forms.ModelChoiceField(required=True, queryset=User.objects.all(), widget=Select(attrs{"class":"form-control"})
class Meta:
model = Customer
will that overide the models salesman field? Must I overide save method to save the newly created field's value to the models default one? Or does django form sees the same name so of field and uses it correctly?

You can redefine if clean method of the form:
class CustomerForm(ModelForm):
class Meta:
model = Customer
def clean(self, *args, **kwargs):
cleaned_data = super(CustomerForm, self).clean(*args, **kwargs)
salesman = cleaned_data.get('salesman')
if salesman:
# do something
else:
# you can rise form error if need
msg = u"Salesman is required"
self._errors["salesman"] = self.error_class([msg])

Related

Show model values as multi select options in admin

I am trying to store use the values from two different models to show as choices in form and store them in new model.
Here is what I have now.
models.py
class Employee(models.Model):
name = models.CharField(max_length=200)
class Product(models.Model):
code = models.CharField(primary_key=True, max_length=5)
class JobQueue(models.Model):
emp_name = models.CharField(max_length=200)
product_code = models.CharField(max_length=5)
forms.py
class JobQueueForm(forms.ModelForm):
emp_choices = Employee._meta.get_field('name').choices
product_code_choices = Product._meta.get_field('code').choices
emp_name = forms.ChoiceField(choices = emp_choices)
product_code =forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=product_code_choices)
def save(self, commit=True):
return super(JobQueueForm, self).save(commit = commit)
class Meta:
model = JobQueue
fields = ('emp_name', 'product_code')
admin.py
class JobQueueAdmin(admin.ModelAdmin):
form = JobQueueForm
fieldsets = (
(None,{
'fields': ('emp_name', 'product_code'),}),
)
def save_model(self, request, obj, form, change):
super(JobQueueAdmin, self).save_model(request, obj, form, change)
admin.site.register(models.Employee, AuthorAdmin)
admin.site.register(models.Product, ProductAdmin)
admin.site.register(models.JobQueue, JobQueueAdmin)
I do have values stored in Employee and Product models, but I dont see them as options in JobQueue model in Admin portal.
Am I missing anything here?
emp_choices = Employee._meta.get_field('name').choices
This line doesn't make sense. It tries to get the choices for the Employee.name field, but you did not specify any choices for the name field in your model.
class Employee(models.Model):
name = models.CharField(max_length=200)
If you want generate a list of choices from all the existing employees in the database, then you can define a method that does this:
def get_names():
return Employee.objects.values_list('name', 'name')
Then use this method in your form:
class JobQueueForm(forms.ModelForm):
emp_name = forms.ChoiceField(choices=get_names)
...

django get existing objects and update m2m

models.py
class PhoneNumber(models.Model):
person = models.ManyToManyField(Person, related_name='phonenumbers', blank=True)
employee = models.ManyToManyField(Employee, related_name='phonenumbers', blank=True)
phone = models.CharField(max_length = 20)
For check exist phone I tried create save() method for PhoneNumber model:
def save(self, *args, **kwargs):
#check if phone exists
exist_phone = PhoneNumber.objects.filter(phone=self.phone).last()
if exist_phone:
new_people = self.person
if new_people:
for person in new_people:
exist_phone.person.add(person)
new_employees = self.employee
if new_employees:
for employee in new_employees:
exist_phone.employee.add(employee)
else:
super(PhoneNumber, self).save(*args, **kwargs)
It doesn't work because an error occurs:
"<PhoneNumber: >" needs to have a value for field "phonenumber" before this many-to-many relationship can be used.
But I want get existing object and add new value for m2m. How do i get values from posted m2m fields? Is there are possible to realize it in model?
Update
It's not possible to realize this in model.
I use drf and realized get_or_create in serializer
class PhoneNumberSerializer(serializers.ModelSerializer):
class Meta:
model = PhoneNumber
fields = ('id', 'person', 'employee', 'phone')
def create(self, validated_data):
people = validated_data.pop('person')
employees = validated_data.pop('employee')
phone = validated_data['phone']
exist_phone = PhoneNumber.objects.filter(phone=phone).last()
if exist_phone:
phonenumber = exist_phone
for person in people:
person.phonenumbers.add(phonenumber)
for employee in employees:
employee.phonenumbers.add(phonenumber)
else:
phonenumber = PhoneNumber.objects.create(**validated_data)
return phonenumber
This is not very convenient, because you need to do the same for both drf and admin and for other forms.
You should use Signal in this case because M2M field can save after creating instance.
I think that you use post_save.
I recommend you read this doc.
https://docs.djangoproject.com/en/1.10/ref/signals/#django.db.models.signals.post_save

How to sort a ChoiceField in a ModelForm?

I have a model that contains a user field.
usr = models.ForeignKey(User, related_name='+', limit_choices_to={'is_active': True})
I have a ModelForm (shown below) that allows the usr to be set: this all works fine. However, the list of users is presented in a random order.
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['usr', ]
How can I sort the list of active users in the drop down?
One option is to set the ordering for your model. In your form, the model choice field should use the same ordering.
class MyModel(models.Model):
...
class Meta:
ordering = ['username']
If you want a different ordering in your model form, then you can use a ModelChoiceField and order the queryset.
class MyModelForm(forms.ModelForm):
usr = forms.ModelChoiceField(Usr.objects.order_by('username'))
class Meta:
model = MyModel
fields = ['usr', ]
The disadvantage of this is you lose information from the model field (e.g. help_text) unless you duplicate it in the form.
To prevent duplication, you can replace the queryset in the __init__ method.
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['usr', ]
def __init__(self, *args, **kwargs):
super(MyModelForm, self).__init__(*args, **kwargs)
self.fields['usr'].queryset = self.fields['usr'].queryset.order_by('username')

Django - Feeding only part of Foreignkey in models

i'm new to django and as a exercise I want to make "home expenses".
I've already did simple model and form :
(from models)
#models.py
#these are the type (groceries/clothes/etc. )
class TypWydatku(models.Model):
typ_wydatku = models.CharField(max_length=25)
data_wpisu=models.DateField(auto_now_add=True)
#these are the actual input with dates/prices/etc
class Wpisy(models.Model):
data_zakupu=models.DateField(default=timezone.now())
data_wpisu=models.DateField(auto_now=True)
typ_wydatku=models.ForeignKey(TypWydatku)
kwota=models.FloatField('kwota')
uwagi=models.CharField(max_length=255, blank=True)
But now I would like to add another model, which will describe expenses connected to my car more specific. So I add another TypWydatku - Moto with id=3
Next step is to create new model with extra fields (mileage/ fuel tanked):
#models.py
(...)
class WpisyMoto(models.Model):
wpis=models.ForeignKey(Wpisy)
przebieg=models.IntegerField()
uwagi=models.CharField(max_length=200)
litry=models.FloatField()
#and more
I have the sipmlest forms as one can have rigth now :
# forms.py
class TypWydatkuForm(ModelForm):
class Meta:
model = TypWydatku
fields = '__all__'
class WpisyForm(ModelForm):
class Meta:
model = Wpisy
fields = '__all__'
class WpisyMotoForm(ModelForm):
class Meta:
model = WpisyMoto
fields = '__all__'
I would like to have choice field 'wpis' in the template, where i want to see onlythose which have 'typ_wydatku'=3. How should I do it ?
From the docs for the ModelChoiceField field (which is the form field type Django will use to represent the ForeignKey field), this can be achieved by setting the form field's queryset attribute:
class WpisyMotoForm(ModelForm):
class Meta:
model = WpisyMoto
fields = '__all__'
def __init__(self, *args, **kwargs):
super(WpisyMotoForm, self).__init__(*args, **kwargs)
self.fields["wpis"].queryset = Wpisy.objects.filter(typ_wydatku__pk=3)

How to override model field validation in django rest framework ModelSerializer

I have the following model:
class UserProfile(models.Model):
mobileNumber = models.BigIntegerField(primary_key=True)
authKey = models.CharField(max_length=300,null=False,blank=False)
creationDateTime = models.DateTimeField(auto_now_add=True)
lastUpdateDateTime = models.DateTimeField(auto_now=True)
Serializer:
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = ('mobileNumber','authKey')
If userprofile model already has a mobilenumber XX44 and if I try to serialize using UserProfileSerializer with json {'mobileNumber': XX44, 'authKey': u'ggsdsagldaslhdkjashdjkashdjkahsdkjah'} I'm getting the following error:
{'mobileNumber': [u'User profile with this MobileNumber already exists.']}
because model validations are being run for the serializer field.
How can I stop execution of model field validation for mobileNumber. I have tried validate and validate_mobileNumber methods in serializer but they still are executing the model validations.
remove unique constraint on mobile number of table,so django serializer will validate according to that .
or
alternatively,
serializer=UserProfileSerializer(data=request.DATA,partial=True)
I understand you won't save the serializer data. So, you can set mobileNumber as a read_only field on UserProfileSerializer.
Check serializer fields documentation for more info: http://www.django-rest-framework.org/api-guide/fields/#core-arguments
By overriding the model field within the serializer, and specifying required=False, allow_blank=True, allow_null=True:
class SomeModel(models.Model):
some_model_field_which_is_required = models.ForeignKey(...)
some_other_required_field = models.CharField(...)
class SomeModelSerializer(serializers.ModelSerializer):
some_model_field_which_is_required = SomeNestedSerializer(
many=True, required=False, allow_blank=True
)
some_other_required_field = serializers.CharField(required=False, allow_blank=True)
def validate(self, *args, **kwargs):
print('should get here')
def validate_some_other_required_field(self, *args, **kwargs):
print('should also get here')
class Meta:
model = SomeModel