custom intermediate table django fetch the records - django-models

I have two models with M2M relation. The custom table is defined as with en extra field
class DoctorHospital(models.Model):
clinic = models.ForeignKey(ClinicHospital, on_delete=models.CASCADE)
doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
shift = models.CharField(max_length = 10)
Problem is that I am trying to fetch all clinics based on a specific doctor. Data is come based on specific doctor but custom field shift does not come.
here is my class base view
class DoctorDetailView(generic.DetailView):
model = Doctor
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['clinic_hospital_list'] = self.object.clinic_hospital.all()
return context

You can annotate the clinic_hospital_list to obtain the related shift value:
from django.db.models import F
class DoctorDetailView(generic.DetailView):
model = Doctor
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['clinic_hospital_list'] = self.object.clinic_hospital.annotate(
shift=F('doctorhospital__shift')
)
return context
Now the ClinicHospitals that arise from this queryset will have an extra attribute .shift that holds the value of the shift field in the intermediate table.

Related

Got AttributeError when attempting to get a value for field `people` on serializer `commentsSerializer`

I am building a blog website and I am using Django rest framework
I want to fetch top 2 comments for a particular post along with their related data such as user details.
Now I have user details in two models
User
People
and the comments model is related to the user model using foreign key relationship
Models ->
Comments
class Comment(models.Model):
comment = models.TextField(null=True)
Created_date = models.DateTimeField(auto_now_add=True)
Updated_date = models.DateTimeField(auto_now=True)
post = models.ForeignKey(Post, on_delete=models.CASCADE,
related_name='comments_post')
user = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='comments_user')
The People model is also connected to the user model with a foreign key relationship
People Model ->
class People(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,related_name='people')
Name = models.CharField(max_length=255,null=True)
following = models.ManyToManyField(to=User, related_name='following', blank=True)
photo = models.ImageField(upload_to='profile_pics', blank=True,null=True)
Phone_number = models.CharField(max_length=255,null=True,blank=True)
Birth_Date = models.DateField(null=True,blank=True)
Created_date = models.DateTimeField(auto_now_add=True)
Updated_date = models.DateTimeField(auto_now=True)
for fetching the comments I am using rest-framework and the serializers look like this
class UserSerializer(serializers.Serializer):
username = serializers.CharField(max_length=255)
class peopleSerializer(serializers.Serializer):
Name = serializers.CharField(max_length=255)
class commentsSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
comment = serializers.CharField(max_length=255)
Created_date = serializers.DateTimeField()
user = UserSerializer()
people = peopleSerializer()
The query to fetch the comments look like this ->
post_id = request.GET.get('post_id')
comments = Comment.objects.filter(post_id=post_id).select_related('user').prefetch_related('user__people').order_by('-Created_date')[:2]
serializer = commentsSerializer(comments, many=True)
return Response(serializer.data)
I am getting this error ->
Got AttributeError when attempting to get a value for field `people` on serializer `commentsSerializer`. The serializer field might be named incorrectly and not match any attribute or key on the `Comment` instance. Original exception text was: 'Comment' object has no attribute 'people'.
Unable to find a way out.
The source is user.people, not people, so:
class commentsSerializer(serializers.Serializer):
# …
people = peopleSerializer(source='user.people')
In the .select_related(…) [Django-doc] to can specify user__people: this will imply selecting user and will fetch the data in the same query, not in an extra query as is the case for .prefetch_related(…) [Django-doc]:
post_id = request.GET.get('post_id')
comments = Comment.objects.filter(
post_id=post_id
).select_related('user__people').order_by('-Created_date')[:2]
serializer = commentsSerializer(comments, many=True)
return Response(serializer.data)
Note: normally a Django model is given a singular name, so Person instead of People.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: normally the name of the fields in a Django model are written in snake_case, not PascalCase, so it should be: created_date instead of Created_date.

Adding data to multiple tables using django rest serializers

I am working on django rest framework coupled with react frontend. I am building a simple web service for a medium-sized organisation.
Here is my Invoice model from models.py
class Invoice(models.Model):
invoice_id = models.AutoField(primary_key=True, editable=False)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
date = models.DateField(auto_now_add=True)
builty_num = models.CharField(max_length=100)
total_amount = models.FloatField(validators=[MinValueValidator(0.0)])
class Meta:
ordering = ['date', 'customer']
InvoiceDetail from models.py
class InvoiceDetail(models.Model):
invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)
warehouse = models.ForeignKey(Warehouse, on_delete=models.SET_NULL, null=True)
rate = models.FloatField(validators=[MinValueValidator(0.0)])
num_thaan = models.PositiveIntegerField()
gazaana_per_thaan = models.FloatField(validators=[MinValueValidator(0.0)])
Ledger model
class Ledger(models.Model):
TYPE = (
('D','Debit'),
('C','Credit'),
)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
date = models.DateField(auto_now_add=True)
detail = models.TextField()
transaction_type = models.CharField(max_length=1, choices=TYPE)
amount = models.FloatField(validators=[MinValueValidator(0.0)])
Now I will be having a form on my front-end which will be sending customer_id and an array of InvoiceDetail which I want to fill my database with. So, I will be using information from that form and use it to fill up multiple entries in my InvoiceDetail and the total_amount would also be calculated from the invoice detail. After this, I would also fill up the ledger based on
the total_amount. Right now, I am unable to figure out a way to do this as I am just able to manipulate a single table via a viewset
P.S. I am using these serializers right now with DefaultRouter()
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = '__all__'
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
class WarehouseSerializer(serializers.ModelSerializer):
class Meta:
model = Warehouse
fields = '__all__'
class ExpenseSerializer(serializers.ModelSerializer):
class Meta:
model = Expense
fields = '__all__'
class InvoiceSerialiser(serializers.ModelSerializer):
class Meta:
model = Invoice
fields = '__all__'
And this is my views.py
class CustomerViewSet(viewsets.ModelViewSet):
serializer_class = CustomerSerializer
queryset = Customer.objects.all()
class ProductViewSet(viewsets.ModelViewSet):
serializer_class = ProductSerializer
queryset = Product.objects.all()
class WarehouseViewSet(viewsets.ModelViewSet):
serializer_class = WarehouseSerializer
queryset = Warehouse.objects.all()
class ExpenseViewSet(viewsets.ModelViewSet):
serializer_class = ExpenseSerializer
queryset = Expense.objects.all()
In Django REST Framework (DRF), by using Nested Relationships of Serializer Relations data can be added to multiple tables having relationships.
In Nested Relationships, the referred entity can be embedded or nested in the representation of the object that refers to it. Such nested relationships can be expressed by using serializers as fields. If we want to support write-operations to a nested serializer field we need to have create() and/or update() methods in order to explicitly specify how the child relationships should be saved. It may be noted that by default nested serializers are read-only.
Below is the implementation of the relationship between Invoice and InvoiceDetail.
class InvoiceDetailSerialiser(serializers.ModelSerializer):
class Meta:
model = InvoiceDetail
fields = "__all__"
read_only_fields = ("invoice", )
It is required to make invoice readonly in InvoiceDetailSerialiser.
class InvoiceSerialiser(serializers.ModelSerializer):
invoice_details = InvoiceDetailSerialiser(many=True)
class Meta:
model = Invoice
fields = ('invoice_id', 'customer', 'date', 'builty_num', 'total_amount', 'invoice_details')
def create(self, validated_data):
invoice_details_data = validated_data.pop('invoice_details')
invoice= Invoice.objects.create(**validated_data)
for invoice_detail_data in invoice_details_data :
InvoiceDetail.objects.create(invoice=invoice, **invoice_detail_data )
return invoice
If the field is used to represent a to-many relationship, we should add the many=True flag to the serializer field.
Based on the above explanation, rest of the implementations can be developed.
More details on this topic from the official guide can be found here.

Serializer field for side effect model django rest framework

I have a django.db.models.Model A whose instances are created in a rest_framework.serializers.ModelSerializer from POST requests.
Depending on the data being sent in the POST, I would like to create one of several other "addon" models, let's say B or C, which I link to the original through a django.db.models.OneToOneField:
from django.db import models
class A(models.Model):
some_field = models.CharField()
class B(models.Model):
a = models.OneToOneField(A, related_name='addon', on_delete=models.CASCADE)
class C(models.Model):
a = models.OneToOneField(A, related_name='addon', on_delete=models.CASCADE)
What I would like to is to have a serializer which validates the incoming data, including some string indicating which addon to use. The serializer then creates the model instance of A and based on this creates the addon model.
I do not want to create a utility field in model A used to determine which addon to use, I would like to create the model directly using the instance of model A and information from the POST itself.
At the same time when accessing the data through a get, I would like to return the original string used to determine which addon to use.
What I have come up with so far:
from rest_framework import serializers
str2model = {'b': B, 'c': C}
class AddonField(serializers.Field):
def to_representation(self, value):
# I completely ignore "value" as no "internal value" is set in "to_internal_value"
myvalue = self.parent.instance.addon
for addon_name, addon_class in str2model.items():
if isinstance(myvalue, addon_class):
return addon_name
def to_internal_value(self, data):
# I create the "internal value" after "A" instance is created, thus here I do nothing?
return data
class ASerializer(serializers.ModelSerializer):
some_field = serializers.CharField()
the_addon = AddonField()
def validate_the_addon(self, value): # here addon is a string
if value in str2model.keys():
return value
def create(self, validated_data):
addon_name = validated_data.pop('the_addon')
addon_class = str2model[addon]
a = super(ASerializer, self).create(validated_data)
addon_class.objects.create(a=a)
return a
class Meta:
model = A
fields = ["some_field", "the_addon"]
When testing this I get:
AttributeError: Got AttributeError when attempting to get a value for field `the_addon` on serializer `ASerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `A` instance.
Original exception text was: 'A' object has no attribute 'the_addon'.
How can I temporarily store the_addon in the serializer until the A instance has been created?
This is how I would typically approach it
# Serializer
class ASerializer(serializers.Serializer):
some_field = serializers.CharField()
addon_b = serializers.CharField(required=False, allow_null=True)
addon_c = serializers.CharField(required=False, allow_null=True)
def create(self, validated_data):
addon_b = validated_data.pop('addon_b')
addon_c = validated_data.pop('addon_c')
a = A.objects.create(some_field=validated_data['some_field'])
if addon_b:
B.objects.create(a=a)
if addon_c:
C.objects.create(a=a)
return a
You can do other validations if necessary.
class TestAPIView01(generics.CreateAPIView):
permission_classes = {}
serializer_class = serializers.ASerializer
queryset = A.objects.all()
Also, look at the related_name on B and C you may want to consider making them different, as that might throw an error in the future. Cheers

Updating existing entity in endpoints-proto-datastore

I am using Endpoints-proto-datastore written by Danny Hermes for Google App Engine and need help figuring out how to update an entity.. My model for what I need to update is the following
class Topic(EndpointsModel):
#_message_fields_schema = ('id','topic_name','topic_author')
topic_name = ndb.StringProperty(required=True)
topic_date = ndb.DateTimeProperty(auto_now_add=True)
topic_author = ndb.KeyProperty(required=True)
topic_num_views = ndb.IntegerProperty(default=0)
topic_num_replies = ndb.IntegerProperty(default=0)
topic_flagged = ndb.BooleanProperty(default=False)
topic_followers = ndb.KeyProperty(repeated=True)
topic_avg_rating = ndb.FloatProperty(default=0.0)
topic_total_rating = ndb.FloatProperty(default=0.0)
topic_num_ratings = ndb.IntegerProperty(default=0)
topic_raters = ndb.KeyProperty(repeated=True)
And as you can see, the rating properties have a default of 0. So each time a topic is rated, I need to update each of the rating properties. However, none of my properties is the actual rating being provided by the user. How can i pass in the value the user rated the topic to be able to update the properties in the model? Thanks!
You can do this by having an "alias" property called rating associated with your UserModel:
from endpoints_proto_datastore.ndb import EndpointsAliasProperty
class UserModel(EndpointsModel):
...
def rating_set(self, value):
# Do some validation
self._rating = value
#EndpointsAliasProperty(setter=rating_set)
def rating(self):
return self._rating
This will allow ratings to be sent with UserModels in requests but won't require those ratings to be stored.
You're better off using the OAuth 2.0 token for the user and calling endpoints.get_current_user() to determine who the user is in the request.
Something like a dedicated model for ratings could be much easier:
from endpoints_proto_datastore.ndb import EndpointsUserProperty
class Rating(EndpointsModel):
rater = EndpointsUserProperty(raise_unauthorized=True)
rating = ndb.IntegerProperty()
topic = ndb.KeyProperty(kind=Topic)
and then transactionally retrieving the Topic from the datastore and updating it in a request method decorated by #Rating.method.

Django m2m 'through' with a generic foreignkey

I have the follow code example, which is a simplified abstraction of a real world project I'm working on:
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class FeatureSet(models.Model):
"""
Feature Set
"""
name = models.CharField(max_length=255)
def __unicode__(self):
return u"%s" % self.name
class GenericObjectAlpha(models.Model):
title = models.CharField(max_length=255)
feature_sets = models.ManyToManyField(FeatureSet, through='Feature')
def __unicode__(self):
return u"%s" % self.title
class GenericObjectBeta(models.Model):
title = models.CharField(max_length=255)
feature_sets = models.ManyToManyField(FeatureSet, through='Feature')
def __unicode__(self):
return u"%s" % self.title
class Feature(models.Model):
"""
Feature
"""
# FK to feature set
feature_set = models.ForeignKey(FeatureSet)
# FK to generic object, Generic object alpha or beta... or others
content_type = models.ForeignKey(
ContentType,
default='article',
limit_choices_to={ 'model__in': ('genericobjectalpha', 'genericobjectbeta') },
related_name="play__feature_set__feature")
object_id = models.PositiveIntegerField(
"Feature object lookup")
content_object = generic.GenericForeignKey(
'content_type',
'object_id')
# Extra fields on a m2m relationship
active = models.BooleanField()
order = models.PositiveIntegerField()
def __unicode__(self):
return u"%s::%s" % (self.feature_set, self.content_object)
This line causes an error:
feature_sets = models.ManyToManyField(FeatureSet, through='Feature')
Obviously because the 'through' model lacks a corresponding FK to each side of the m2m. What I'd like to achieve here, is that one side of the m2m relationship is generic, and, that I can specify my own intermediary join table, to do the usual adding of custom fields etc.
What are my options for accomplishing this?
Note, its currently an important requirement to include the feature_sets = models.ManyToManyField(FeatureSet, through='Feature') line in the generic model, mostly for admin UI purposes. The reason why its generic is that its not yet determined how many models this line will be placed upon.

Resources