Django rest framework foreign key constraint fails creating model - python

I have the following in a Django Rest Framework setup:
models.py:
class Sku(BaseModel):
sku_code = models.CharField(max_length=18, primary_key=True)
supplier_id = models.PositiveIntegerField(db_index=True)
soh = models.PositiveIntegerField(default=0)
reserved = models.PositiveIntegerField(default=0)
broken = models.PositiveIntegerField(default=0)
unallocated = models.PositiveIntegerField(default=0)
reorder = models.PositiveIntegerField(default=0)
class Reservation(BaseModel):
sku = models.ForeignKey(Sku, db_column='sku_code')
order_id = models.PositiveIntegerField(db_index=True)
serializers.py:
class SkuSerializer(serializers.ModelSerializer):
class Meta:
model = Sku
fields = (
'sku_code',
'supplier_id',
'soh',
'reserved',
'broken',
'unallocated',
'reorder',
'created_at',
'modified_at',
)
class ReservationSerializer(serializers.ModelSerializer):
sku = SkuSerializer(read_only=True)
class Meta:
model = Reservation
fields = (
'id',
'order_id',
'sku',
'created_at',
'modified_at'
)
views.py:
class ReservationList(mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Reservation.objects.all()
serializer_class = ReservationSerializer
def post(self, request, *args, **kwargs):
sku = get_object_or_404(Sku, sku_code=request.data['sku_code'])
request.data['sku'] = sku
return self.create(request, *args, **kwargs)
Now when I post to the url linked to ReservationList.post view above I get the error: IntegrityError: (1048, "Column 'sku_code' cannot be null").
It seems to be bypassing the serializers validation and failing on the database layer. For some reason it doesn't accept the SKU being passed in.
What am I doing wrong here? I have tried to follow the example at http://www.django-rest-framework.org/api-guide/relations/#nested-relationships but this seems to break down with the CreateModelMixin. I can't tell if there is something wrong with how my models or serializers are set up.

You've set the sku field to read only, that's why the serializer is ignoring it when you post.
From the relevant documentation
Read-only fields are included in the API output, but should not be
included in the input during create or update operations. Any
'read_only' fields that are incorrectly included in the serializer
input will be ignored.

Related

How to get model data to appear as a field in another model's response

These are simplified versions of my models (the user model is just an id and name)
class Convo(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='convo_owner')
users = models.ManyToManyField(User, through='Convo_user')
class Convo_user (models.Model):
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
convo = models.ForeignKey(Convo, on_delete=models.CASCADE)
class Comments(models.Model):
name = models.CharField(max_length=255)
content = models.TextField(max_length=1024)
convo = models.ForeignKey(Convo, on_delete=models.CASCADE)
This is my view
class ConvoViewSet(viewsets.ModelViewSet):
serializer_class = serializers.ConvoSerializer
def get_queryset(self):
return None
def list(self, request):
curr_user = request.user.id
# Collecting the list of conversations
conversations = models.Conversation.object.filter(ConvoUser__user_id=request.user.id)
#Getting list of conversation id's
conv_ids = list(conversations.values_list('id', flat=True).order_by('id'))
#Getting list of relevant comments
comments = models.Comments.objects.filter(conversation_id__in=conv_ids)
return Response(self.get_serializer(conversations, many=True).data)
And my current serializer
class ConvoSerializer(serializers.ModelSerializer):
"""A serializer for messaging objects"""
# access = AccessSerializer(many=True)
# model = models.Comments
# fields = ('id', 'name', 'content', 'convo_id')
class Meta:
model = models.Convo
fields = ('id', 'owner_id')
The current response I get is of the form
[
{
"id": 1,
"owner_id": 32
}, ...
]
But I would like to add a comments field that shows all the properties of comments into the response, so basically everything in the second queryset (called comments) and I'm not sure how to go about this at all. (I retrieve the comments in the way I do because I'm trying to minimize the calls to the database). Would I need to create a new view for comments, make its own serializer and then somehow combine them into the serializer for the convo?
The way you've set up your models, you can access the comments of each Convo through Django's ORM by using convo_object.comments_set.all(), so you could set up your ConvoSerializer to access that instance's comments, like this:
class ConvoSerializer(serializers.ModelSerializer):
"""A serializer for messaging objects"""
comments_set = CommentSerializer(many=True)
class Meta:
model = models.Convo
fields = ('id', 'owner_id', 'comments_set')
and then you define your CommentSerializer like:
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = models.Comments
fields = ('id', 'name', 'content')
No data appears because my serializers are using the default database, not sure why but a step forward
EDIT:
Django: Database used for prefetch_related is not the same that the parent query Provided me the correct answer, I was able to choose the database with this method because for some reason inner queries use the default DB

Django REST: Serializer lookup by UUID

I'm creating this simple shopping API in Django REST.
Internally I'm using IDs for foreign key constraints, while guuids are brought to the outside world.
For the checkout procedure, the user provides a list of article IDs he is willing to purchase. The object in the POST data thus looks as follows:
{
assets: [
{
'product': 'd9d5044d-2284-4d15-aa76-2eee3675035b',
'amount': 4
},
....
]
}
I'm using the following ticket/asset models:
# Ticket
class Ticket(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='tickets', on_delete=models.CASCADE)
# Assets
class Asset(models.Model):
ticket = models.ForeignKey(Ticket, related_name='assets', on_delete=models.CASCADE)
stock_item = models.ForeignKey(Stock, related_name='stock_item', on_delete=models.SET_NULL, null=True)
amount = models.IntegerField(validators=[MinValueValidator(0)])
And the serializers look as follows:
# Asset serializer
class AssetSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
fields = ('stock_item', 'amount')
# Ticket serializer
class TicketSerializer(WritableNestedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
assets = AssetSerializer(many=True)
class Meta:
model = Ticket
fields = ('uuid', 'owner', 'assets', )
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
When posting an object of the type specified above, the following error is presented:
{"assets":[{"stock_item": ["Invalid type. Expected PK, received string"]}]}
Which I can't seem to solve, how do I instruct the serializer to use the uuid as the lookup value? I solved a similar problem on view-level earlier by using the lookup_field member, but that doesn't seem to solve it. Any suggestions?
Enter code here
If I have understood you correctly, a SlugRelatedField should be able to find the correct related object.
class AssetSerializer(serializers.ModelSerializer):
ticket = serializers.SlugRelatedField(
read_only=True,
slug_field='uuid',
queryset=Ticket.objects.all() # Might be redundant with read_only=True
)
class Meta:
model = Asset
fields = ('ticket', 'stock_item', 'amount')
Elaborating on #BjornW's comment:
class UUIDRelatedField(serializers.SlugRelatedField):
slug_field = 'uuid'
def __init__(self, **kwargs):
super().__init__(slug_field=self.slug_field, **kwargs)
def to_representation(self, obj):
return getattr(obj, self.slug_field).hex

Related field id in POST request in Django Rest Framework

I am creating the object with just only related field id. I have searched a lot but couldn't get the answer. Here is my code
models.py:
class Resturant(models.Model):
name = models.CharField(_('name'), max_length=100)
class Menu(models.Model):
resturant_id = models.OneToOneField(Resturant, related_name='resturant', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now=True)
serializers.py:
class MenuSerializer(serializers.ModelSerializer):
resturant_id = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Menu
fields = ['id', 'created_at', 'resturant_id']
views.py:
class CreateMenuAPIView(APIView):
def post(self, request, *args, **kwargs):
serializer = MenuSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(status=status.HTTP_400_BAD_REQUEST)
I am getting this error while sending { "resturant_id": 2 } in POST request.
DETAIL: Failing row contains (14, 2018-04-02 09:36:43.261849+00, null).
The above exception (null value in column "resturant_id" violates not-null constraint
Any help would be appreciated !
you can override method create for find Restaurant object or create if not exist. and only edit serializer.
serializer.py
class MenuSerializer(serializers.ModelSerializer):
resturant_id = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Menu
fields = ['id', 'created_at', 'resturant_id']
def create(self, validated_data):
id_param = validated_data.pop('resturant_id')
resturant = Resturant.objects.get_or_create(id=id_param)[0]
menu = Menu.objtects.create(resturant_id=resturant.id)
return menu
if not work you can delete this line:
resturant_id = serializers.PrimaryKeyRelatedField(read_only=True)
returant_id = serializers.PrimaryKeyRelatedField(read_only=True)
Could you try giving read_only=False
Could you check the spelling,
returant_id is used in serializers field,'s' is missing.
'resturant_id' is used in fields list
You are using a model serializer and have overridden 'returant_id'.
class MenuSerializer(serializers.ModelSerializer):
returant_id = serializers.PrimaryKeyRelatedField(queryset=Resturant.objects.all())
try to change the serializer to
class MenuSerializer(serializers.ModelSerializer):
resturant_id = serializers.PrimaryKeyRelatedField()
class Meta:
model = Menu
fields = ['id', 'created_at', 'resturant_id']
if read_only=True then it will not write into database.

How can I write multiple filter in Django

I am confusing how can I write the filter code in my serializers.py.
I have the following models.
User (AbstractBaseUser)
userid = models.CharField(max_length=64, primary_key=True)
username = models.CharField(max_length=128)
Clinic (models.Model)
clinic_id = models.CharField(max_length=8, primary_key=True)
clinic_name = models.CharField(max_length=64)
Consultation (models.Model)
consultation_id = models.AutoField(primary_key=True)
clinic_id = models.ForeignKey(Clinic)
user = models.ForeignKey(User)
Chat (models.Model)
chat_id = models.AutoField(primary_key=True)
consultation_id = models.ForeignKey(Consultation)
DIDRESPONSE_CHOICE = {
('R', 'No'),
('C', 'Doing'),
('F', 'Done'),
}
did_response = models.CharField(max_length=1, choices=DIDRESPONSE_CHOICE, default='N')
ChatDetail (models.Model)
chat_no = models.AutoField(primary_key=True)
chat_id = models.ForeignKey(Chat)
CHATFROM_CHOICE = {
('D', 'Doctor'),
('U', 'User'),
}
chat_from = models.CharField(max_length=1, blank=True, null=True)
chat = models.CharField(max_length=512, blank=True)
I want to serialize above data and response to client in JSON format.
{
'username': 'Tom Smith',
'clinic_name': 'Adam's clinic',
'consultation_id': 12345678,
'chat_id': 09876543,
'chat_detail' : [
{'chat_no': 11122233, 'chat_from': 'D', 'chat': 'How are you doing today?'},
{'chat_no': 11112234, 'chat_from': 'U', 'chat': 'I have a headache'},
{'chat_no': 11122235, 'chat_from': 'D', 'chat': 'Oh, I'm sorry'},
{'chat_no': 11122236, 'chat_from': 'U', 'chat': 'Can you help me?'},
]
}
I made filters in my views.py by using override get_queryset method. But it wasn't not work. So, I tried to write filters in my serializers.py. But it is also didn't work(I don't understand how can I write above requirement filter in my serializers.py).
What should I do to achieve my requirement? Please help me please.
views.py
class WebViewChatDataOutputView(APIView):
def get(self, request, format=None):
chat_detail = ChatDetail
serializer = WebViewChatDataOutputSerializer(chat_detail, many=True)
filter_fields = ('chat_id')
return Response(serializer.data)
def get_queryset(self):
consultation_id = self.request.consultation_id
chat = Chat.objects.filter(consultation_id=consultation_id)
return ChatDetail.objects.filter(chat_id=chat.chat_id)
serializers.py
class WebViewChatDataOutputSerializer(serializers.ModelSerializer):
# class Meta:
# model = Consultation
# fields = ('consultation_id')
#
# class Meta:
# model = Chat
# fields = ('chat_id')
#
class Meta:
chat = ChatDataForChatDetailSerializer(many=True)
model = ChatDetail
fields = ('chat_no', 'chat_from', 'chat')
I am trying to make a filter. But I got 'Meta.fields'; contains fields that are not defined on this FilterSet: ChatDetail, Chat" error.
My view is the following.
from .filtering import CategoryFilter
class WebViewChatDataOutputView(generics.RetrieveAPIView):
def get(self, request, format=None):
filter_backends = (
rest_framework.filters.OrderingFilter,
rest_framework.filters.DjangoFilterBackend
)
filter_class = CategoryFilter
serializer = WebViewChatDataInputSerializer(chat_detail, many=True)
return Response(serializer_class.data)
And my filtering.py is the following.
class CategoryFilter(rest_framework.filters.FilterSet):
name = django_filters.CharFilter(
name=ChatDetail,
lookup_expr='chat_id'
)
parent_category_name = django_filters.CharFilter(
name=Chat,
lookup_expr='chat_id'
)
class Meta:
model = ChatDetail
fields = ['ChatDetail', 'Chat']
Did I mistake any kind of grammar? Please gime an advice.
I updated my code.
views.py
class WebViewChatDataOutputView(generics.RetrieveAPIView):
def get(self, request, format=None):
lookup_field = 'chat_id'
queryset = Chat.objects.all()
serializer_class = ChatSerializer
return Response(serializer_class.data)
serializers.py
class ChatDetailSerializer(serializers.ModelSerializer):
class Meta:
model = ChatDetail
fields = '__all__'
class ChatSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='consultation_id.user.username') # You should rename the consultation_id foreign key field to consultation, by the way
clinic_name = serializers.CharField(source='consultation_id.clinic_id.clinic_name') # Same goes for clinic_id
chat_detail = ChatDetailSerializer(many=True, source='chatdetail_set')
class Meta:
model = Chat
fields = ('user_name', 'clinic_name', 'consultation_id', 'chat_id', 'chat_detail')
I put below couple solution to achieve filters for your endpoint.
You can use filter_class but to do it use ListApiView. Create filtering.py and add there something(for quick example):
import django_filters
import rest_framework.filters
from categories import models as categories_models
class CategoryFilter(rest_framework.filters.FilterSet):
name = django_filters.CharFilter(
name='name',
lookup_expr='icontains'
)
parent_category_name = django_filters.CharFilter(
name='parent_category__name',
lookup_expr='icontains'
)
class Meta:
model = categories_models.Category
fields = ['name', 'parent_category_name']
In your view add:
import rest_framework.filters
filter_backends = (
rest_framework.filters.OrderingFilter,
rest_framework.filters.DjangoFilterBackend
)
filter_class = categories_filtering.CategoryFilter
More details you find in ListApiView inspector
Its clear because you have another file only with filter logic and its seperated from your serializer.
You can use also **get_queryset but first u need add statment to check url for example:**
url contain q parametr with filter field
in method u need get q value and to something with these
write query to do it
but better idea is to use(u need change to Generic):
Filter_queryset
It seems to me that your problem is not filtering, in the DRF sense or the QuerySet sense, you simply need the right serializers and views for your models.
The output you shared refers to a given Chat. For the serializers, you'll need a ChatSerializer and a ChatDetailSerializer (for the list of ChatDetails).
Your can use the source argument in your serializer fields to retrieve data from your related models, so ChatSerializer could look something like:
class ChatSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='consultation_id.user.username') # You should rename the consultation_id foreign key field to consultation, by the way
clinic_name = serializers.CharField(source='consultation_id.clinic_id.clinic_name') # Same goes for clinic_id
chat_detail = ChatDetailSerializer(many=True, source='chatdetail_set')
class Meta:
model = Chat
fields = ('user_name', 'clinic_name', 'consultation_id', 'chat_id', 'chat_detail')
The ChatDetailSerializer can be a simple ModelSerializer
class ChatDetailSerializer(serializers.ModelSerializer):
class Meta:
model = ChatDetail
fields = '__all__'
In your views, DRF needs to know how to fetch the data. You can use get_queryset for that, but that does nothing if you implement in on an ApiView, use a RetrieveView instead, since you're showing one model instance (a ChatDetail). Or use some other generic view depending on your needs (see the DRF documentation on the subject, http://www.django-rest-framework.org/api-guide/generic-views/#retrieveapiview).
class WebViewChatDataOutputView(generics.RetrieveAPIView):
lookup_field = 'chat_id'
queryset = Chat.objects.all()
serializer_class = ChatSerializer
And that's it. I hope you figure it out.

Django Rest Framework: dynamic database on POST - RelatedField or PrimaryKeyRelatedField

I'm developing RESTFul services with DRF and I have multiple databases depending on the country (see my last question here)
I'm having a problem now with relationships, I have two models: Category and SubCategory:
class SubCategory(models.Model):
objects = CountryQuerySet.as_manager()
id = models.AutoField(primary_key=True,db_column='sub_category_id')
name = models.TextField()
female_items_in_category = models.BooleanField()
male_items_in_category = models.BooleanField()
kids_items_in_category = models.BooleanField()
category = models.ForeignKey('Category')
class Meta:
managed = True
db_table = Constants().SUBCATEGORY
And the serializer is:
class SubCategorySerializer(serializers.ModelSerializer):
category = PrimaryKeyRelatedField(queryset=Category.objects.using('es').all())
class Meta:
model = SubCategory
fields = ('id', 'name','female_items_in_category','male_items_in_category','kids_items_in_category','category')
If I don't set the queryset with the proper country it fails, because it doesn't know where to get the category.
Here the problem
I already set the country in the serializer context (in the ModelViewSet):
def get_serializer_context(self):
return {Constants().COUNTRY: self.kwargs.get(Constants().COUNTRY)}
But I can not find the proper way to get the self.context.get(Constants().COUNTRY) in the serializer.
Do you any have an idea to solve this? Thanks!
Well, I found a solution to my problem: I overwrite the method get_fields in the serializer:
def get_fields(self, *args, **kwargs):
fields = super(SubCategorySerializer, self).get_fields()
country = self.context.get(Constants().COUNTRY)
qs = Category.objects.using(country).all()
fields['category'].queryset = qs
return fields
And that works!

Categories

Resources