Django Rest Framework not validating all model's fields - django

I searched for this problem everywhere without being able to find an answer though it seems basic DRF usage, so I might be missing sth.
I have a Customer model with certain required fields:
from django.db import models
from django.utils.translation import gettext_lazy as _
from applications.core.models.country import Country
from applications.core.models.customer_states.customer_state import \
CustomerState
class Customer(models.Model):
class Meta:
verbose_name = _('customer')
verbose_name_plural = _('customers')
user_email = models.EmailField(_('email'), max_length=100, unique=True, default=None)
complete_name = models.CharField(_('complete name'), max_length=200, default=None)
phone = models.CharField(_('phone'), max_length=50, default=None)
country = models.ForeignKey(Country, models.PROTECT, verbose_name=_('country'), default=None)
city = models.CharField(_('city'), max_length=100, default=None)
city_state = models.CharField(_('city state'), max_length=100, default=None)
address = models.CharField(_('address'), max_length=100)
zip_code = models.CharField(_('zip code'), max_length=50, default=None)
customer_state = models.OneToOneField(CustomerState, models.PROTECT)
notes = models.TextField(_('notes'), max_length=200, blank=True, null=True)
And I have this serializer:
from rest_framework import serializers
from applications.core.models.customer import Customer
from applications.core.models.customer_states.implementations.pending_manual_validation_state import \
PendingManualValidationState
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = '__all__'
def to_internal_value(self, data):
self.add_default_state_if_missing(data)
return super(CustomerSerializer, self).to_internal_value(data)
#staticmethod
def add_default_state_if_missing(data):
data['customer_state'] = PendingManualValidationState.objects.create().pk
Though I have explicitly told DRF that it should use all model's fields it does not seem to check for the requirement of fields like 'address' and whenever I create the serializer with data missing 'address' and call serializer.is_valid() it returns True.
Why?

Found the answer by myself:
It would seem that default=None in the field makes DRF realize that even if the field is required, not receiving data for it is not a problem.
I set those default because otherwise Django would set a '' default value and thus, PostgreSQL would not raise exceptions for those empty data fields. But now that I am starting to use DRF for validations I no longer need those default.
Yet, if I happen to create and save a Customer without using the serializer, then I risk letting Django create a field with a default '' without having PostgreSQL complain about it. I figure that risk has a low probability though.

Related

Strange module behavior djangoql

We use djangoql for easy search in our django admin panel.
The mixin DjangoQLSearchMixin has been added to some of our models in the admin panel.
And sometimes after deployment we get an error in the handler
application_name/model_name/introspect/
Error:
FieldDoesNotExist at /admin/user/user/introspect/
Model_name has no field named 'field_name'
After the reboot, the error disappears. The error cannot be reproduced locally.
Example:
"Address has no field named 'membership_requests'"
#admin.register(MembershipRequest, site=admin_site)
class MembershipRequestAdmin(DjangoQLSearchMixin, admin.ModelAdmin):
list_display = ("company", "user", "request_type", "status", "created_on", "updated_on")
class MembershipRequest(PureModelMixin):
company = models.ForeignKey("constrafor.Company", on_delete=models.CASCADE, related_name="membership_requests")
user = models.ForeignKey("user.User", on_delete=models.CASCADE, related_name="membership_requests")
address = models.OneToOneField(
"constrafor.Address",
related_name="membership_requests",
on_delete=models.CASCADE,
null=True,
blank=True,
help_text="FK to constrafor.Address",
)
code = models.ForeignKey(
"constrafor.Code", on_delete=models.SET_NULL, related_name="membership_requests", blank=True, null=True
)
company_name = models.CharField(null=True, blank=True, max_length=1000)
company_phone = models.CharField(null=True, blank=True, max_length=15)
company_type = models.CharField(max_length=15, choices=Company.COMPANY_TYPE_CHOICES)
is_needed_email_verification = models.BooleanField(default=False)
status = models.CharField(
max_length=8,
choices=MembershipRequestStatus.choices,
default=MembershipRequestStatus.pending,
)
request_type = models.CharField(
max_length=10,
choices=MembershipRequestType.choices,
default=MembershipRequestType.natural,
)
As I remarked in an earlier comment on your question, this seems to be a very tricky heisenbug. Not being able to properly debug it, as it cannot be reproduced, I found a way around it:
class CustomDjangoQLSchema(DjangoQLSchema):
def get_field_instance(self, model, field_name):
"""Some obscure heisenbug caused introspect requests to raise, rendering DjangoQL useless.`
This catch the exception and just skip the problematic field.
"""
try:
return super().get_field_instance(model, field_name)
except FieldDoesNotExist:
return None
If you use this Schema instead of the default you should be able to skip those failing fields.
UPDATE: Later I found out that the model that caused trouble was not registered with Django properly. It makes sense that a model that is not imported in models.__init__ but that does have a referral causes unexpected behaviour like this.

Attribute Error in Django model Foreign Key

In My Django Project, there are two apps: Login and Company
The error that am receiving in this is
AttributeError: module 'login.models' has no attribute 'Country'
Company App > models.py
from django.db import models
from login import models as LM
class CompanyProfile(models.Model):
full_name = models.CharField(max_length=255,
unique = True)
country = models.ForeignKey(LM.Country,
on_delete=models.SET_NULL,
null=True,
blank=False)
state = models.ForeignKey(LM.State,
on_delete=models.SET_NULL,
null=True,
blank=False)
def __str__(self):
return self.full_name
Login App > models.py
class Country(models.Model):
"""List of Country"""
name = models.CharField(max_length=50, unique= True, default='None')
code = models.CharField(max_length=2, unique= True, primary_key=True, default ='NA')
def __str__(self):
return str(self.code)
class State(models.Model):
"""List fo State"""
region = models.CharField(max_length = 255, unique = True, primary_key=True, default='None')
country = models.ForeignKey(Country, on_delete=models.SET_NULL, null=True, blank=False, default ='NA')
def __str__(self):
return self.region
Here is test to check that weather is login is getting imported or not
def test_import():
try:
# import pdb; pdb.set_trace()
importlib.find_loader('LM.Country')
found = True
except ImportError:
found = False
print(found)
Answer is received stands to be True
python3 manage.py shell
>>> test_import()
True
Now on other stackoverflow blogs i checked i thought it could be of Circlular Import
But i have already fixed that still am getting this error?
Thanks in Advance
Regards
I am not able to see any issue here technically. Maybe Django doesn't support this alias way of mentioning model as Foreign Key which I have never tried this way.
But I would suggest to use string format for adding Foreign Key of other model as below.
class CompanyProfile(models.Model):
full_name = models.CharField(max_length=255, unique = True)
# In following line, as I mention model name in string which django understands
country = models.ForeignKey('login.Country', on_delete=models.SET_NULL,
null=True,blank=False)
Another way is simple import but it might be a problem in case of circular depedencies. So I don't recommend to use that.
I hope you get the answer out of it.

after I created multiple models in my django app for the restframwork I start getting more errs

I created a single model django project then I added tow more models then I got this err
RecursionError at /elements/
maximum recursion depth exceeded while calling a Python object
then I reinstalled some packages like djangorestframwork-recursion, then I got this err
Error at /
Incorrect padding
Request Method: GET
Request URL: http://127.0.0.1:8000/
Django Version: 3.0.8
Exception Type: Error
Exception Value:
Incorrect padding
Exception Location: //anaconda3/lib/python3.7/base64.py in b64decode, line 87
Python Executable: //anaconda3/bin/python
Python Version: 3.7.3
Python Path:
['/Users/apple/Desktop/Trying to clone notionso note taking app with django '
'and reac1/backend/restapi',
'//anaconda3/lib/python37.zip',
'//anaconda3/lib/python3.7',
'//anaconda3/lib/python3.7/lib-dynload',
'/Users/apple/.local/lib/python3.7/site-packages',
'//anaconda3/lib/python3.7/site-packages',
'//anaconda3/lib/python3.7/site-packages/aeosa']
Server time: Fri, 7 Aug 2020 06:59:57 +0000
also when I ran python manage.py makemigrations i got
You are trying to change the nullable field 'text' on components to non-nullable without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Ignore for now, and let me handle existing rows with NULL myself (e.g. because you added a RunPython or RunSQL operation to handle NULL values in a previous data migration)
3) Quit, and let me add a default in models.py
Select an option:
what is the problem with the nullable field 'text' (also I change every null=Flase to null=True i got the same err.
models.py
# this is a recursive model
from django.db import models
class elements(models.Model):
tag = models.CharField(blank=True, null=True, max_length=20)
text = models.TextField(blank=True, null=False)
src = models.URLField(blank=True, null=True)
# problem: styles replace spaces with slashes (/)
# I tried: json field but did'nt work.
style = models.TextField(blank=True, null=True)
main = models.ForeignKey(
'self', null=True, blank=True, related_name="sub", on_delete=models.PROTECT)
class styles(models.Model):
name = models.TextField(blank=True, null=True, max_length=50)
style = models.TextField(blank=True, null=True)
main = models.ForeignKey(
'self', null=True, blank=True, related_name="sub", on_delete=models.PROTECT)
class components(models.Model):
name = models.TextField(blank=True, null=True, max_length=50)
tag = models.CharField(blank=True, null=True, max_length=20)
text = models.TextField(blank=True, null=False)
src = models.URLField(blank=True, null=True)
style = models.TextField(blank=True, null=True)
main = models.ForeignKey(
'self', null=True, blank=True, related_name="sub", on_delete=models.PROTECT)
serialzers.py
from rest_framework import serializers
from models.models import elements, styles, components
from rest_framework_recursive.fields import RecursiveField
class elementsSerializer(serializers.ModelSerializer):
sub = RecursiveField(many=True, required=False)
class Meta:
model = elements
fields = ('id', 'tag', 'text', 'src', 'style', 'main', 'sub')
class stylesSerializer(serializers.ModelSerializer):
sub = RecursiveField(many=True, required=False)
class Meta:
model = styles
fields = ('id', 'name', 'style', 'main', 'sub')
class componentsSerializer(serializers.ModelSerializer):
sub = RecursiveField(many=True, required=False)
class Meta:
model = components
fields = ('id', 'name', 'tag', 'text', 'src', 'style', 'main', 'sub')
view.py
from models.models import elements, styles, components
from .serializers import elementsSerializer, stylesSerializer, componentsSerializer
from rest_framework import viewsets
class elementsVeiwSet(viewsets.ModelViewSet):
serializer_class = elementsSerializer
queryset = elements.objects.all()
class stylesVeiwSet(viewsets.ModelViewSet):
serializer_class = stylesSerializer
queryset = styles.objects.all()
class componentsVeiwSet(viewsets.ModelViewSet):
serializer_class = componentsSerializer
queryset = components.objects.all()
urls.py
from models.models import elements, styles, components
from .serializers import elementsSerializer, stylesSerializer, componentsSerializer
from rest_framework import viewsets
class elementsVeiwSet(viewsets.ModelViewSet):
serializer_class = elementsSerializer
queryset = elements.objects.all()
class stylesVeiwSet(viewsets.ModelViewSet):
serializer_class = stylesSerializer
queryset = styles.objects.all()
class componentsVeiwSet(viewsets.ModelViewSet):
serializer_class = componentsSerializer
queryset = components.objects.all()
1)
I am not sure why you have this field in each of your models
main = models.ForeignKey('self', null=True, blank=True, related_name="sub", on_delete=models.PROTECT)
I think this is the reason for your recursion problem. Try removing it.
2) Regarding your problem with the nullable Field text it is because you did not provide a default value (you have to do that because you did not allow it to be Null). You do it like that:
text = models.TextField(blank=True, null=False, default='')

Django 1.11 many to many does not appear in django admin

Hi i have a django model for notification which have a many-to-many relation but nothing appears in django admin ( all fields do not appear)
class Notification(models.Model):
"""send notification model"""
title = models.CharField(max_length=200)
text = models.TextField(null=True, blank=True)
device = models.ManyToManyField(Device, null=True, blank=True)
country = models.ManyToManyField(Country, null=True, blank=True)
sent = models.BooleanField(default=False)
when i open django admin for this model and press add notification this is what happens (nothing appears)
Country and Device Code
class Device(models.Model):
"""Store device related to :model:`accounts.User`."""
user = models.OneToOneField(User, related_name='device', on_delete=models.CASCADE)
model = models.CharField(max_length=200, blank=True, null=True)
player_id = models.CharField(max_length=200, blank=True, null=True)
class Meta:
verbose_name = 'Device'
verbose_name_plural = 'Devices'
def __str__(self):
return self.model
class Country(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
Admin.py
from django.contrib import admin
from .models import Notification
admin.site.register(Notification)
Edit:
Thank you all the problem is solved
The problem was caused by some entries in device model that did have None in the model field so there was a problem displaying it correctly.
According to https://code.djangoproject.com/ticket/2169 :
When a class has a field that isn't shown in the admin interface but
must not be blank, it's impossible to add a new one. You get a cryptic
"Please correct the error below." message with no shown errors. The
error message should probably say something about a hidden field.
Now ManyToManyFields don't need null=True, try removing those statements and see if you get an improvement.
Also, try adding the Country and Device models in admin.py so admin can see them and display them.
https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#working-with-many-to-many-models
Define an inline for the many-to-manys in admin.py:
from django.contrib import admin
class DeviceInline(admin.TabularInline):
model = Notification.device.through
class CountryInline(admin.TabularInline):
model = Notification.country.through
class NotificationAdmin(admin.ModelAdmin):
inlines = [
DeviceInline, CountryInline
]
exclude = ("device", "country")

Properly using Foreign Key references in search_fields, Django admin

I've got a weird conundrum that I need some help with in Django 1.8.4 using python 3.4 in a virtual-env.
I've got 2 models in 2 different apps... as follows with multiple Foreign Key references.
Inventory App
class InventoryItem(models.Model):
item_unique_code = models.CharField(max_length=256, blank=False, null=False)
category = models.CharField(max_length=256, blank=False, null=False,choices=[('RAW','Raw Material'),('FG','Finished Good'),('PKG','Packaging')])
name = models.CharField(max_length=64, blank=False, null=False)
supplier = models.CharField(max_length=96, blank=False,null=False)
approved_by = models.CharField(max_length=64, editable=False)
date_approved = models.DateTimeField(auto_now_add=True, editable=False)
comments = models.TextField(blank=True, null=True)
def __str__(self):
return "%s | %s | %s" % (self.item_unique_code,self.name,self.supplier)
class Meta:
managed = True
unique_together = (('item_unique_code', 'category', 'name', 'supplier'),)
Recipe App
class RecipeControl(models.Model):
#recipe_name choice field needs to be a query set of all records containing "FG-Finished Goods"
recipe_name = models.ForeignKey(items.InventoryItem, related_name='recipe_name', limit_choices_to={'category': 'FG'})
customer = models.ForeignKey(customers.CustomerProfile, related_name='customer')
ingredient = models.ForeignKey(items.InventoryItem, related_name='ingredient')
min_weight = models.DecimalField(max_digits=16, decimal_places=2, blank=True, null=True)
max_weight = models.DecimalField(max_digits=16, decimal_places=2, blank=True, null=True)
active_recipe = models.BooleanField(default=False)
active_by = models.CharField(max_length=64, editable=False)
revision = models.IntegerField(default=0)
last_updated = models.DateTimeField(auto_now_add=True, editable=False)
def __str__(self):
return "%s" % (self.recipe_name)
class Meta:
managed = True
unique_together = (('recipe_name', 'customer', 'ingredient'),)
I've been getting some weird results in my Recipe's Admin class...
from django.contrib import admin
from django.contrib.auth.models import User
from .models import RecipeControl
from Inventory import models
class RecipeView(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.active_by = request.user.username
obj.save()
fieldsets = [
('Recipe Information', {'fields': ['recipe_name', 'customer']}),
('Ingredients', {'fields': ['ingredient','min_weight','max_weight','active_recipe']}),
('Audit Trail', {'fields': ['active_by','revision','last_updated'],'classes':['collaspe']}),
]
list_select_related = ['recipe_name','customer','ingredient']
search_fields = ['recipe_name','customer','ingredient','active_by']
readonly_fields = ('last_updated','active_by')
list_display = ['recipe_name','customer','ingredient','min_weight','max_weight','last_updated','active_by', 'active_recipe']
admin.site.register(RecipeControl, RecipeView)
The issue I've come across is if I try to do a search on any ForeignKey field, Django throws this error...
Exception Type: TypeError at /admin/Recipe/recipecontrol/
Exception Value: Related Field got invalid lookup: icontains
According to the Django Admin Doc's and other older questions on stackoverflow on the subject it says I should be doing something along the lines of search_fields = ['inventoryitem__name'] but I think this is in reference to FK's in the same app model.py.
Is there a more correct way of referencing/importing other models from other apps that I'm missing or do I have to use some kind of callable method magic to get the search function to look up correctly? I've tried a multitude of different combinations and nothing seems to work. I'm relatively new to Django so I'm confident it's something simple.
You should use the double underscore notation to search a field on a related object. However, you should use the name of the foreign key field (e.g. recipe_name), not the name of the model (e.g. InventoryItem). It doesn't matter whether or not the foreign key's model is in the same app. For example:
search_fields = ['recipe_name__name']
Note that if you want to search the recipe_name and ingredient fields, you need to include both fields, even though they are foreign keys to the same model.
search_fields = ['recipe_name__name', 'ingredient__name']