django foreign key cannot be null - python

I am a beginner to django and trying to create a post request on django rest-framework.
I have a following model:
class ProjectScheme(models.Model):
name = models.CharField(max_length=250, blank=False,null=False)
parent_scheme_id = models.ForeignKey(ProjectSchemeMaster, on_delete = models.CASCADE)
rule = models.TextField(blank=True)
def __str__(self):
return str(self.name)
And a serializer:
class SchemeDetailSerializer(serializers.ModelSerializer):
class Meta:
model = ProjectScheme
fields = ('id', 'name', 'parent_scheme_id', 'rule')
depth=1
And my view:
#api_view(['POST'])
#renderer_classes((JSONRenderer,))
def create_project_scheme(request):
if request.method == 'POST':
data = JSONParser().parse(request)
serializer = SchemeDetailSerializer(data=data)
comp_logger.info('Call to create_project')
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response({'response':serializer.errors})
return Response({})
With post request body as:
{
"name": "django-rf"
}
This gives serializer.is_valid() to true, but in response I get
(1048, "Column 'parent_scheme_id_id' cannot be null")
I tried adding parent_scheme_id = models.ForeignKey(ProjectSchemeMaster, on_delete = models.CASCADE, blank=False, null=False) but that didn't make any difference.
How can I validate the request input so that it shows proper validation message like for name field?

In your model, you set your ForeignKey field as a required field, Django by default consider a field required=True if not explicitely provide null=True. So if you want to create ProjectScheme instance without a ForeignKey referrence, then make sure you provide null=True to your ForeignKey field,
class ProjectScheme(models.Model):
name = models.CharField(max_length=250, blank=False,null=False)
parent_scheme_id = models.ForeignKey(ProjectSchemeMaster, null=True, blank=True, on_delete = models.CASCADE)
rule = models.TextField(blank=True)
def __str__(self):
return str(self.name)
in mention, blank=True works in form level validation.

Related

Django filtering ForeignKey

This is view:
def post(self, request):
author_request = request.data.get("author")
queryset = Book.objects.filter(author=author_request)
serializer = BookSerializer(queryset, None)
return Response(serializer.data, HTTP_200_OK)
This is model:
class Author(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
full_name = models.CharField(max_length=255, null=True, blank=True)
about = models.TextField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.full_name
class Meta:
verbose_name = 'Author'
verbose_name_plural = 'Authors'
So, when i try to filter the book by author i get the error. The error tells me that the POST data that i entered which is "Aleksa Petrovic" a name of a author that exists in the database is not an UUID. So when i filter it, it filters by UUID and i want it to filter by "full_name"
You can .filter(…) [Django-doc] with:
Book.objects.filter(author__full_name=name_of_author)
One can use double underscores (__) to look "through" relations.
The view thus looks like:
def post(self, request):
author_request = request.data.get('author')
queryset = Book.objects.filter(author__full_name=author_request)
serializer = BookSerializer(queryset, None)
return Response(serializer.data, HTTP_200_OK)
Typically searches with a filter are not handled by a POST request, but by a GET request, since a GET request is supposed to retrieve data from the web server.

DRF 'create' method deletes fields from 'validated_data'

I want to realize POST method to create Article object.
I have to provide this body:
{
"title": "Sample title",
"link": "https://www.sample.com/"
}
And get Article object with auto assigned ID, author_name(current user) and creation_date.
But i got an problem - "create" methor deletes one field - author_name. (See code below)
Finally i get next error msg:
IntegrityError at /api/articles/
Error: Null value in column "author_name_id" with NOT NULL
DETAIL: Error row contains (32, Sample title, https://www.sample.com/, 2020-11-08, null).
How do i solve my problem?
models.py
class Article(models.Model):
title = models.CharField(
name='title',
max_length=120,
null=False,
)
link = models.URLField(
name='link',
unique=True,
null=False,
)
creation_date = models.DateField(
name='creation_date',
default=date.today,
null=False,
)
author_name = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='articles',
null=False,
)
def __str__(self):
return self.title
views.py
class ArticleView(APIView):
def post(request):
if request.user.is_authenticated:
data = {"author_name": request.user}
data.update(request.data)
print(request.data)
serializer = ArticleSerializer(data=data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(status=201)
return Response(status=403)
request.data >>> {'title': 'Sample title', 'link': 'https://www.sample.com/', 'author_name': <SimpleLazyObject: <User: admin>>}
serializers.py
class ArticleSerializer(serializers.ModelSerializer):
author_name = serializers.StringRelatedField()
class Meta:
model = Article
fields = "__all__"
def create(self, validated_data):
print(validated_data)
return Article.objects.create(**validated_data)
validated_data >>> {'title': 'Sample title', 'link': 'https://www.sample.com/'}
I think it is because of the author_name = serializers.StringRelatedField() you have on your serializers. By default this is a read-only field so the serializer will remove it from the data you pass to it.
I'm pretty sure if you would just do:
serializer = ArticleSerializer(request.data)
if serializer.is_valid():
serializer.save(author_name=request.user)
this should solve your problem. Because on the serializer you validate the fields that are not read-only and then you save the data by passing the user instance to the save method of the serializer.
I also see you are using an ApiView for creating a model instance, I would recommend looking into CreateApiView

Django save ManyToManyField Model with ModelMultipleChoiceField

So I know there are a few similar questions but none of the solutions worked for me. I've tried save(commit=false) and save_m2m as well as a bunch of other stuff but i get the error
NOT NULL constraint failed: home_services.managers_id
Anyways here's my code:
views.py
def service(request):
if request.user.is_authenticated:
if request.method == 'POST': #Create Service
form = CreateServiceForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/service') #Later change this to redirect to server page
else:
form = CreateServiceForm()
args = {'user': request.user, 'form': form}
return render(request, 'service.html', args)
else:
return HttpResponseRedirect('/feed')
models.py
class Services(models.Model):
name = models.CharField(max_length=100, default='')
description = models.CharField(max_length=500, default='')
owner = models.OneToOneField(User, on_delete=models.CASCADE)
managers = models.ForeignKey(User, related_name="managers", on_delete=models.CASCADE)
members = models.ManyToManyField(User, related_name="members")
def __str__(self):
return str(self.name) + ": id" + str(self.id)
forms.py
class CreateServiceForm(forms.ModelForm):
owner = forms.ModelChoiceField(queryset=User.objects.all())
members = forms.ModelMultipleChoiceField(queryset=User.objects.all())
class Meta:
model = Services
fields = [
'name',
'description',
'owner',
'members',
]
I want the user to be able to create a service and select 1 or more members that is in the default django User model.
I want the user to be able to create a service and select 1 or more members that is in the default django User model.
You are not providing managers, which is not allowed as per the current configuration.
So, change:
managers = models.ForeignKey(
User,
related_name="managers",
on_delete=models.CASCADE,
)
To:
managers = models.ForeignKey(
User,
related_name="managers",
on_delete=models.CASCADE,
blank=True,
null=True,
)
You might want to read more about blank and null.

Django Rest Framework number type still pass validation from model

Hi i'm currently trying to write validation for my update API. i used the model fields in my serializer like so as i only want these fields to be update in the custom User model:
class UpdateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = (
'email', 'uid', 'nickname', 'eth_address', 'eth_private_key', 'evt_address', 'evt_private_key'
)
My User model:
Class User(AbstractUser):
uid = models.CharField(
"uid", max_length=255, null=True, blank=True)
phone_number = models.CharField(
"Phone number", max_length=255, null=True, blank=True)
nickname = models.CharField(
"Nickname", max_length=255, null=True, blank=True)
status = models.PositiveSmallIntegerField("Status", default=0)
eth_address = models.CharField(
"Eth address", max_length=255, null=True, blank=True)
eth_private_key = models.CharField(
"Eth private key", max_length=255, null=True, blank=True)
evt_address = models.CharField(
"Evt address", max_length=255, null=True, blank=True)
evt_private_key = models.CharField(
"Evt private key", max_length=255, null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
# deleted
class Meta:
db_table = "users"
pass
And here is my DRF update model view:
class update_profile(RetrieveUpdateAPIView):
permission_classes = ()
authentication_classes = (FirebaseAuthentication,)
def update(self, request):
user_data = request.data
user = User.objects.get(uid=request.user.uid)
serializer = UpdateUserSerializer(user, data=user_data, partial=True)
if user_data == {}:
raise serializers.ValidationError({'code': 400, 'data': 'Invalid JSON object'})
for key_name in user_data:
if key_name not in ['email', 'uid', 'nickname', 'eth_address', 'eth_private_key', 'evt_address', 'evt_private_key']:
raise serializers.ValidationError({'code': 400, 'data': 'Invalid input ' + key_name})
if serializer.is_valid():
updated_user = serializer.save()
return JsonResponse({'code': 200,'data': updated_user.uid}, status=200)
else:
return JsonResponse({'code': 400,'errors':serializer.errors}, status=400)
My input is as follow on POSTMAN:
{
"email": "test#gmail.com",
"uid": "dqwdqwd3123123",
"nickname":"Alan",
"eth_address": "dwdw",
"eth_private_key": "fwef",
"evt_address": "dwqdqwf",
"evt_private_key": "dwqdqqd"
}
I want to test with conditions of wrong input so i input wrong email like wrongemail.com and it validated correctly:
{
"code": 400,
"errors": {
"email": [
"Enter a valid email address."
]
}
}
But when i try to do wrong input to other type like "nickname": 4123414, it still pass validation even when in the model i set nickname = models.CharField("Nickname", max_length=255, null=True, blank=True) i don't know why the input value of number passed the validation without raising error.
First of all, I think most of the stuff you are doing here, like checking for correct fields, are done by the serializer itself. You just have to use is_valid(raise=True) and django will take care of returning the proper HTTP response (if you are using the default django error handler - read more in the docs)
So here is how your view should look like:
class update_profile(RetrieveUpdateAPIView):
permission_classes = ()
authentication_classes = (FirebaseAuthentication,)
def update(self, request):
user_data = request.data
user = User.objects.get(uid=request.user.uid)
serializer = UpdateUserSerializer(user, data=user_data, partial=True)
if serializer.is_valid(raise_exception=True):
updated_user = serializer.save()
return JsonResponse({'code': 200,'data': updated_user.uid}, status=200)
However, I also think the nick example is actually correct. Number can be cast as string and that's what the serializer is doing -> it changes 4123414 to '4123414'. If you think about it, it's a totally valid nick. You should try other examples, like providing a negative number for status.

Alllow one extra filed into serializer and return validated data with that field in Django Rest Framework

I know on this topic people asked a question before but my case is different and I have implemented almost all the solutions which I found but none of them are worked for me.
First I have defined three classes in models:
models.py
class User(AbstractBaseUser, PermissionsMixin):
""" User Model """
username = None
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
agency = models.ForeignKey('agency.Agency', on_delete=models.CASCADE, null=True)
weekly_email = models.NullBooleanField()
is_create_new_password = models.NullBooleanField(default=True)
is_active = models.BooleanField(default=True)
last_login_time = models.DateTimeField(null=True)
last_login_from = models.CharField(max_length=255, null=True)
created_at = models.DateField(default=timezone.now)
updated_at = models.DateField(default=timezone.now)
created_by = models.IntegerField(null=True)
updated_by = models.IntegerField(null=True)
""" The `USERNAME_FIELD` property tells us which field we will use to log in.
In this case, we want that to be the email field. """
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["username"]
""" Tells Django that the UserManager class defined above should manage
objects of this type. """
objects = UserManager()
class Role(models.Model):
""" Role Model """
name = models.CharField(max_length=255, unique=True)
class UserRole(models.Model):
""" User Role Model """
user = models.ForeignKey(User, on_delete=models.CASCADE)
role = models.ForeignKey(Role, on_delete=models.CASCADE)
Then I have defined my serializer for user module:
serializers.py
class RegistrationSerializer(serializers.ModelSerializer):
""" Serializers registration requests and creates a new user. """
user_id = serializers.IntegerField(required=False)
email = serializers.EmailField(max_length=255)
name = serializers.CharField(max_length=255)
agency_id = serializers.IntegerField(source='agency.id', required=False)
role = serializers.CharField(source='role.name')
weekly_email = serializers.NullBooleanField()
last_login_time = serializers.DateTimeField(required=False)
last_login_from = serializers.CharField(max_length=255, required=False)
class Meta:
model = User
fields = (
'role', 'user_id', 'email', 'name', 'agency_id', 'weekly_email', 'last_login_time',
'last_login_from'
)
And At the end, I have defined my view file for user creation:
views.py
class UserCreateAPIView(APIView):
""" User create Api view class """
#Allow any user (authenticated or not) to hit this endpoint.
permission_classes = (IsAuthenticated,)
serializer_class = RegistrationSerializer
def post(self, request):
""" create user using following logic. """
request.data['user_id'] = request.user.id
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(user=request.user)
return Response({'message': response['user']['created'], 'data': serializer.data},
status=status.HTTP_201_CREATED)
Now when I run it everything works fine like user is created, role is created as per my expectations. My view, serializer and models excuted but at the end on this line:
return Response({'message': response['user']['created'], 'data': serializer.data},
status=status.HTTP_201_CREATED)
I am facing error like,
AttributeError: Got AttributeError when attempting to get a value for field `role` on serializer `RegistrationSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.
Original exception text was: 'User' object has no attribute 'role'.
I think you need to use ModelSerializer
class RegistrationSerializer(serializers.Serializer):
to
class RegistrationSerializer(serializers.ModelSerializer):

Categories