I'm trying to display the attributes in the Disease Model and Evidence Model, but the attributes that is displayed on the end link are only those attributes that are present in the Rule Model.
Models.py :-
class Rule(models.Model):
disease = models.ForeignKey(Disease, default=0,related_name="DRules")
evidence = models.ForeignKey(Evidence, default=0,related_name="ERules")
measure_of_belief = models.PositiveIntegerField( \
help_text="The measure of belief (percentage) that a disease is present given this evidence exists", \
default=0,validators=[MinValueValidator(0), MaxValueValidator(100)])
measure_of_disbelief = models.PositiveIntegerField( \
help_text="The measure of disbelief (percentage) that a disease is present given an evidence does not exists", \
default=0,validators=[MinValueValidator(0), MaxValueValidator(100)])
archived = models.BooleanField(default=False)
def __str__(self):
return "{}-{}".format(self.disease, self.evidence)
class Meta:
verbose_name = "Rule"
verbose_name_plural = "Rules"
unique_together = ('disease', 'evidence',)
class Disease(models.Model):
"""
The model where the category will be stored
"""
name = models.CharField(max_length=255)
advise = models.CharField(max_length=500,blank=True)
archived = models.BooleanField(default=False)
def __str__(self):
return self.name
class Meta:
verbose_name = "Disease"
verbose_name_plural = "Diseases"
class Evidence(models.Model):
evidence_choices = {
("Observable Evidence", ("Observable Evidence")),
("Cause", ("Cause"))
}
name = models.CharField(max_length=255)
question = models.CharField(max_length=500)
evidence_type = models.CharField(choices = evidence_choices,max_length=20,default="Observable Evidences")
archived = models.BooleanField(default=False)
image_name = models.ImageField(upload_to='media/', default='media/None/no-img.jpg')
def __str__(self):
return self.name
class Meta:
verbose_name = "Evidence"
verbose_name_plural = "Evidences"
Serializers.py
class DiseaseSerializer(serializers.ModelSerializer):
class Meta:
model = Disease
fields = '__all__'
class EvidenceSerializer(serializers.ModelSerializer):
class Meta:
model = Evidence
fields = '__all__'
class RuleSerializer(serializers.ModelSerializer):
DRules = DiseaseSerializer(many=True,read_only=True)
ERules = EvidenceSerializer(many=True,read_only=True)
class Meta:
model = Rule
fields = ('measure_of_belief','disease','evidence','DRules','ERules')
views.py:-
class ShowDiseaseProfile(APIView):
def get(self,request,profileid):
profile = Rule.objects.filter(
disease_id=profileid)
serializer = RuleSerializer(
profile,many=True)
return Response(serializer.data)
What I'm trying to accomplish is to display all data that is present on the disease, evidence, and rule.
There is no error or crashing, the value from the disease model and evidence model is just not displaying.
Here's the result
EDIT: previous answer was incorrect. Updated the answer.
You are using incorrect field names in your serializer. Below is the correct one:
class RuleSerializer(serializers.ModelSerializer):
disease = DiseaseSerializer(many=True,read_only=True)
evidence = EvidenceSerializer(many=True,read_only=True)
class Meta:
model = Rule
fields = ('measure_of_belief','disease','evidence','DRules','ERules')
related_name param on the foreign key field is used for reverse lookups. Checkout the docs for a proper explanation. For example:
disease = Disease.objects.get(id=200)
d_rules = disease.DRules.all()
Use select_related query to optimize the sql query. Select related uses a join to fetch the data in one sql operation.
class ShowDiseaseProfile(APIView):
def get(self,request,profileid):
profile = (Rule.objects.filter(disease_id=profileid)
.select_related('disease', 'evidence'))
serializer = RuleSerializer(
profile,many=True)
return Response(serializer.data)
Related
I have a short model to store football teams and results of their matches. The Match model has a home_team_id and an away_team_id to identify the 2 teams, both of them are the Foreign Key for the Team model. I want to serialize the data from these 2 tables to have the following output:
[
{
"home_team_id": 283584,
"home_team_name": "FC Bayern München"
"away_team_id": 61,
"away_team_name": "Chelsea FC"
"match_id": 12342
"home_team_goals": 1,
"away_team_goals": 2,
},
...
]
models.py
from django.db import models
class Team(models.Model):
team_id = models.IntegerField(primary_key=True)
team_name = models.CharField(max_length=200, blank=False, null=False)
#category_id = models.IntegerField(blank=False)
#value = models.IntegerField(blank=False)
def __str__(self):
return '%s' % (self.team_name)
class Match(models.Model):
match_id = models.IntegerField(primary_key=True)
home_team_id = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="home_team_id")
away_team_id = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="away_team_id")
home_team_goals = models.SmallIntegerField(blank=False)
away_team_goals = models.SmallIntegerField(blank=False)
views.py
#api_view(['GET'])
def full_list(request):
full = Match.objects.all()
serializer = TeamMatchesSerializer(full, many=True)
return Response(serializer.data)
serializers.py
class TeamMatchesSerializer(serializers.ModelSerializer):
team_name = serializers.StringRelatedField(many=True, read_only=True)
class Meta:
model = Match
fields = ('match_id', 'home_team_goals', 'away_team_goals', 'team_name')
I am receiving an error: 'Match' object has no attribute 'team_name', which is fair as in views.py the Match model has been selected,however I added team_name = serializers.StringRelatedField(many=True, read_only=True) in serializers. I tried this the other way around, with using the Team model in the serializer, based on the tutorial here: https://www.django-rest-framework.org/api-guide/relations/, but it didn't work. I am not even sure how to differentiate the team names as it both uses the Team model.
The raw SQL this query just to make sure my question makes sense:
select home.team_id,
home.team_name,
away.team_id,
away.team_name,
a.match_id,
a.home_team_goals,
a.away_team_goals
from api_match a
inner join api_team home on home.team_id = a.home_team_id
inner join api_team away on away.team_id = a.away_team_id
The solution is Simple whenever you want some computed value in your serializer, you can use serializers.SerializerMethodField() docs. In Your case, you can define your serializer as
class TeamMatchesSerializer(serializers.ModelSerializer):
home_team_name = serializers.SerializerMethodField()
away_team_name = serializers.SerializerMethodField()
def get_home_team_name(self, obj):
return obj.home_team_id.team_name
def get_away_team_name(self, obj):
return obj.away_team_id.team_name
class Meta:
model = Match
fields = '__all__'
Also, a suggestion from my side, when declaring the attribute name for foreign key fields, don't use the _id suffix at the end because the ORM does it for you.
Before this gets marked as a duplicate, I've looked at the following questions:
Django Rest framework Serialize many to many field
Django rest framework serializing many to many field
No results are being shown for payins when I test.
serializers.py
class PayinsSerializer(serializers.ModelSerializer):
class Meta:
model = Payins
fields = '__all__'
class UserStokvelSerializer(serializers.ModelSerializer):
payins = PayinsSerializer(many=True)
class Meta:
model = Stokvel
fields = '__all__'
models.py
class Payins(models.Model):
payin_date = models.DateField(default=timezone.now)
payin_by = models.ForeignKey(User, on_delete=models.CASCADE)
stokvel_payin = models.ForeignKey('Stokvel', on_delete=models.CASCADE, related_name="payin_user")
payin_amount = models.IntegerField()
def save(self, *args, **kwargs):
stokvel = Stokvel.objects.get(id = self.stokvel_payin.id)
stokvel.balance += self.payin_amount
stokvel.save()
super(Payins, self).save(*args, **kwargs)
class Stokvel(models.Model):
stokvel_name = models.CharField(max_length=55)
balance = models.IntegerField(default = 0)
payouts = models.ManyToManyField(Payouts, related_name="stokvel_payouts")
payins = models.ManyToManyField(Payins, related_name="stokvel_payins")
group_admin = models.ForeignKey(User, on_delete=models.CASCADE, related_name="stokvel_group_admin")
users = models.ManyToManyField(User, related_name="stokvel_users")
invite_key = models.CharField(default = secrets.token_hex(15), max_length=55)
Was able to figure it out using this guide:
By modifying this example:
class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Track
fields = ('order', 'title')
class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
I am trying to create a Many to Many relation with a model in between, I have a Client model, and a Zone model, each client may have access to different zones, and each zone may have multiple clients.
Therefore I created a model called Access Permission, that stores said relation, and I want to show a dropdown selector in the post form that shows the existing clients and zones, or to ask for the Id of an existing object, instead of showing the form to create new ones.
These are my models:
class Zone(models.Model):
name = models.TextField()
created = models.DateTimeField(auto_now=True)
def __str__(self):
return '%s' % (self.name)
class Client(models.Model):
name = models.TextField()
birthDate = models.DateField()
created = models.DateTimeField(auto_now=True)
def __str__(self):
return '%s' % (self.name)
class AccessPermission(models.Model):
idClient = models.ForeignKey(Client, on_delete=models.CASCADE, null=False)
idZone = models.ForeignKey(Zone, on_delete=models.CASCADE, null=False)
And these my current serializers:
class ZoneSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Zone
fields = ('name',)
class ClientSerializer(serializers.HyperlinkedModelSerializer):
zones = ZonesSerializer(source='accesspermission_set', many=True, read_only=True)
class Meta:
model = Client
fields = ('name', 'birthDate', 'zones')
class AccessPermissionSerializer(serializers.ManyRelatedField):
idClient = ClientSerializer(many=False)
idZone = ZoneSerializer(many=False)
class Meta:
model = AccessPermission
fields = ('idClient', 'idZone')
Is there any way to ask for the Id of an existing object, or show the existing ones, instead of the fields to create new ones?
You can do it like:
models
class AccessPermission(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE, null=False)
zone = models.ForeignKey(Zone, on_delete=models.CASCADE, null=False)
serializers
class AccessPermissionSerializer(serializers.ManyRelatedField):
id = serializers.IntegerField(read_only=True)
client_id = serializers.PrimaryKeyRelatedField(
queryset=Client.objects.all(), source='client', allow_null=False, required=True
)
zone_id = serializers.PrimaryKeyRelatedField(
queryset=Zone.objects.all(), source='zone', allow_null=False, required=True
)
class Meta:
model = AccessPermission
fields = (
'id', 'client_id', 'zone_id'
)
I'm building a read API for an existing (legacy) SQL database using Django Rest Framework. The problem is that way too many queries are made.
I want to select a Widget and its (about 40) related widgets, including the widget_technology for each of the related widgets. The technology of a widget depends on the presence or absence of a certain tag (I realize it's suboptimal database design but it's legacy...). The problem is that this results in 40+ queries (one per related widget).
Is there any way to fix it?
I've been looking at prefetch_related('widget_tags') in the DRF part of the code, but that doesn't seem to help.
Here is the main table:
class Widgets(models.Model):
widget_id = models.AutoField(primary_key=True)
widget_slug = models.CharField(unique=True, max_length=70)
widget_name = models.CharField(max_length=70)
widget_tags = models.ManyToManyField('Tags', through='IsTag')
related_widgets = models.ManyToManyField('Widgets', through='Similar', related_name='similar')
#property
def widget_technology(self):
if self.widget_tags.filter(tag_slug='tech_1').exists():
return 'TECH_1'
elif self.widget_tags.filter(tag_slug='tech_2').exists():
return 'TECH_2'
class Meta:
managed = False
db_table = 'widgets'
For completeness, here are the other tables:
class Similar(models.Model):
first_widget = models.ForeignKey(
Widgets, models.DO_NOTHING, primary_key=True, related_name='similar_first_widget'
)
second_widget = models.ForeignKey(
Widgets, models.DO_NOTHING, related_name='similar_second_widget'
)
score = models.IntegerField()
class Meta:
managed = False
db_table = 'similar'
unique_together = (('first_widget', 'second_widget'),)
class IsTag(models.Model):
is_tag_widget = models.ForeignKey(Widgets, models.DO_NOTHING, primary_key=True)
is_tag_tag = models.ForeignKey('Tags', models.DO_NOTHING)
class Meta:
managed = False
db_table = 'is_tag'
unique_together = (('is_tag_widget', 'is_tag_tag'),)
class Tags(models.Model):
tag_id = models.AutoField(primary_key=True)
tag_slug = models.CharField(max_length=100)
class Meta:
managed = False
db_table = 'tags'
EDIT: Adding the View
class WidgetDetail(APIView):
def get_object(self, slug):
try:
return Widgets.objects.get(widget_slug=slug)
# tried this but didn't solve the issue:
# return Widgets.objects.prefetch_related('related_widgets', 'related_widgets__widget_tags').get(widget_slug=slug)
except Widgets.DoesNotExist:
raise Http404
def get(self, request, slug, format=None):
widget = self.get_object(slug)
serializer = FullWidgetSerializer(widget)
return Response(serializer.data)
and the serializers:
class FullWidgetSerializer(serializers.ModelSerializer):
# special cases
widget_tags = SimpleTagSerializer(many=True, read_only=True)
related_widgets = SimpleWidgetSerializer(many=True, read_only=True)
class Meta:
model = Widgets
fields = (
'widget_slug', 'related_widgets', 'widget_tags', 'widget_technology')
class SimpleWidgetSerializer(serializers.ModelSerializer):
class Meta:
model = Widgets
fields = ('widget_slug', 'widget_technology')
I am writing a simple database for the condo I live in which has a list of people, units, unit type (home vs parking space), and unitholder (join table for many-to-many relationship between a person and a unit) - one person can be the owner of a unit type of "home" while renting a parking space.
This is my model:
class Person(models.Model):
first_name = models.CharField(max_length=30, null=False)
last_name = models.CharField(max_length=30, null=False)
phone = models.CharField(max_length=20)
email = models.EmailField(max_length=20)
class UnitType(models.Model):
description = models.CharField(max_length=30)
class Unit(models.Model):
unit_number = models.IntegerField(null=False, unique=True)
unit_type = models.ForeignKey(UnitType, null=False)
unitholders = models.ManyToManyField(Person, through='UnitHolder')
class UnitHolderType(models.Model):
description = models.CharField(max_length=30)
class UnitHolder(models.Model):
person = models.ForeignKey(Person)
unit = models.ForeignKey(Unit)
unitholder_type = models.ForeignKey(UnitHolderType)
This is my view:
class PersonViewSet(viewsets.ModelViewSet):
queryset = Person.objects.all()
serializer_class = PersonSerializer
class UnitHolderTypeViewSet(viewsets.ModelViewSet):
queryset = UnitHolderType.objects.all()
serializer_class = UnitHolderTypeSerializer
class UnitViewSet(viewsets.ModelViewSet):
queryset = Unit.objects.all()
serializer_class = UnitSerializer
class UnitHolderViewSet(viewsets.ModelViewSet):
queryset = UnitHolder.objects.all()
serializer_class = UnitHolderSerializer
class UnitTypeViewSet(viewsets.ModelViewSet):
queryset = UnitType.objects.all()
serializer_class = UnitTypeSerializer
This is my serializer:
class UnitSerializer(serializers.ModelSerializer):
unit_type = serializers.SlugRelatedField(
queryset=UnitType.objects.all(), slug_field='description'
)
class Meta:
model = Unit
fields = ('unit_number', 'unit_type', 'unitholders')
class UnitTypeSerializer(serializers.ModelSerializer):
class Meta:
model = UnitType
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
class UnitHolderSerializer(serializers.ModelSerializer):
person = serializers.PrimaryKeyRelatedField(many=False, read_only=True)
unit = serializers.PrimaryKeyRelatedField(many=False, read_only=True)
class Meta:
model = UnitHolder
fields = ('person', 'unit', 'unitholder_type')
class UnitHolderTypeSerializer(serializers.ModelSerializer):
class Meta:
model = UnitHolderType
The problem:
When I query the /units endpoint like the following:
u = requests.get('http://localhost:8000/units').json()
My response looks like this:
[{'unit_type': 'Home', 'unit_number': 614, 'unitholders': [1]}]
What I want back is something like this:
[
{
'unit_type': 'Home',
'unit_number': 614,
'unitholders': [
{
'id: 1,
'first_name': 'myfirstname',
'last_name': 'mylastname',
'unitholder_type': 'renter'
}
]
}
]
I'm pretty sure my problem is in my UnitSerializer but I am brand new to DRF and read the through the documentation but still can't seem to figure it out.
An easy solution would be using depth option:
class UnitSerializer(serializers.ModelSerializer):
unit_type = serializers.SlugRelatedField(
queryset=UnitType.objects.all(), slug_field='description'
)
class Meta:
model = Unit
fields = ('unit_number', 'unit_type', 'unitholders')
depth = 1
This will serialize all nested relations 1 level deep. If you want to have fine control over how each nested field gets serialized, you can list their serializers explicitly:
class UnitSerializer(serializers.ModelSerializer):
unit_type = serializers.SlugRelatedField(
queryset=UnitType.objects.all(), slug_field='description'
)
unitholders = UnitHolderSerializer(many=True)
class Meta:
model = Unit
fields = ('unit_number', 'unit_type', 'unitholders')
Also as a side note, you need to look into modifying your querysets inside views to prefetch related objects, otherwise you will destroy the app performance very quickly (using something like django-debug-toolbar for monitoring generated queries is very convenient):
class UnitViewSet(viewsets.ModelViewSet):
queryset = Unit.objects.all().select_related('unit_type').prefetch_related('unitholders')
serializer_class = UnitSerializer
Perhaps you must doing somethings so:
class UnitHolderViewSet(viewsets.ModelViewSet):
queryset = UnitHolder.objects.all()
unitholders = UnitHolderSerializer(read_only=True, many=True)
Django rest framework serializing many to many field