Вывод связанных данных из Django в Drf
Гайз, застакался в вопросе и нужна помощь Есть бэк на django Как вы видите таблицы education и experience связаны с таблицей person. И я в drf пытаюсь вывести все эти связанные данные таким образом, чтобы в объекте person были объекты experience и education, но так как person в себе не хранит ссылки ни на education, ни на experience, у меня это сделать не получается(Я могу вывести объект person в объектах experience и education, а наоборот не получается). Подскажите в какую сторону смотреть, буду благодарен любой помощи.
Вот модель данных models.py
class Person(models.Model):
"""
Our general model
"""
person_hash_index = models.CharField(max_length=100, primary_key=True, null=False)
first_name = models.CharField(max_length=150)
surname = models.CharField(max_length=150)
url = models.CharField(max_length=500)
location = models.CharField(max_length=150, blank=True, db_index=True) # country
city = models.CharField(max_length=150, blank=True, null=True, db_index=True)
last_position = models.CharField(max_length=150, blank=True, db_index=True)
current_company = models.CharField(max_length=1500, blank=True, null=True, db_index=True)
event = models.CharField(max_length=100, blank=True, db_index=True)
event_date = models.CharField(max_length=100, blank=True)
total_duration = models.FloatField(blank=True, default=0, null=True, db_index=True)
updated = models.CharField(max_length=100, blank=True, null=True)
def get_absolute_url(self):
"""
Absolute url for our template
:return: redirect to person_view page
"""
return reverse('person_view', kwargs={'pk': self.pk})
def __str__(self):
return self.first_name
class Education(models.Model):
"""
Person education table
This table are inherited from Person table
"""
person = models.ForeignKey('Person', on_delete=models.CASCADE, related_name='person_education')
edu_id = models.CharField(max_length=100, primary_key=True, null=False)
university = models.CharField(max_length=500)
degree = models.CharField(max_length=500)
info = models.CharField(max_length=65000, blank=True)
admission_date = models.CharField(max_length=500)
graduation_date = models.CharField(max_length=500)
def __str__(self):
return '%s, %s, %s, %s, %s' % (
self.university, self.degree, self.info, self.admission_date, self.graduation_date
)
class Experience(models.Model):
"""
Person Experience table
This table are inherited from Person table
"""
exp_id = models.CharField(max_length=100, primary_key=True, null=False)
person = models.ManyToManyField('Person', through='KeyRelationExp', related_name='person_experience')
person_relation = models.ForeignKey('Person',
on_delete=models.CASCADE,
blank=True, null=True,
related_name='person_relation_experience')
company = models.CharField(max_length=150)
position = models.CharField(max_length=150)
hiring_date = models.CharField(max_length=150)
fired_date = models.CharField(max_length=150)
job_location = models.CharField(max_length=150)
description = models.CharField(max_length=65535, blank=True)
duration = models.FloatField() # mb datetime
exp_index = models.CharField(max_length=50, blank=True)
keywords = models.CharField(max_length=2000, blank=True, null=True)
area = models.CharField(max_length=2000, blank=True, null=True)
def __str__(self):
return '%s, %s, %s, %s, %s, %s' % (
self.company, self.position, self.hiring_date, self.fired_date, self.job_location, self.description
)
class Meta:
ordering = ['exp_id']
class Comments(models.Model):
"""
User comment
This table are inherited from Person table
"""
person = models.ForeignKey('Person', on_delete=models.CASCADE, blank=True, null=True,
related_name='person_comments')
parent = models.ForeignKey('self', on_delete=models.SET_NULL, blank=True, null=True)
name = models.CharField(max_length=50, blank=True, null=True)
content = models.TextField()
publish = models.DateTimeField(auto_now_add=True)
status = models.BooleanField(default=True)
class Meta:
ordering = ['publish']
def __str__(self):
return f'Comment by {self.name}'
class Keyword(models.Model):
keyword_hash = models.CharField(max_length=100, primary_key=True)
person = models.ManyToManyField('Person',
through='KeyRelationPerson',
related_name='person_keyword')
experience = models.ManyToManyField('Experience', through='KeyRelationExp', related_name='person_keyword_experience')
keyword = models.CharField(max_length=2000, blank=True, null=True, db_index=True)
def __str__(self):
return self.keyword
class Area(models.Model):
keyword_hash = models.CharField(max_length=100, primary_key=True)
person = models.ManyToManyField('Person',
through='KeyRelationPerson',
related_name='person_area')
keyword = models.CharField(max_length=2000, blank=True, null=True, db_index=True)
def __str__(self):
return self.keyword
class KeyRelationPerson(models.Model):
person = models.ForeignKey('Person', on_delete=models.CASCADE, blank=True, null=True)
keyword = models.ForeignKey('Keyword', on_delete=models.CASCADE, blank=True, null=True)
area = models.ForeignKey('Area', on_delete=models.CASCADE, blank=True, null=True)
tag_order = models.IntegerField(blank=True, null=True)
class KeyRelationExp(models.Model):
person = models.ForeignKey('Person', on_delete=models.CASCADE, blank=True, null=True)
exp = models.ForeignKey('Experience', on_delete=models.CASCADE, blank=True, null=True)
keyword = models.ForeignKey('Keyword', on_delete=models.CASCADE, blank=True, null=True)
tag_order = models.IntegerField(blank=True, null=True)
Вот мой код serializers.py
from .models import Person, Experience, Education, Keyword, Area
from django_elasticsearch_dsl_drf.serializers import DocumentSerializer
from rest_framework import serializers
from .documents import PersonDocument
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = '__all__'
class EducationSerializer(serializers.ModelSerializer):
person_id = serializers.CharField(write_only=True)
person = PersonSerializer(read_only=True)
class Meta:
model = Education
fields = '__all__'
class ExperienceSerializer(serializers.ModelSerializer):
class Meta:
model = Experience
fields = '__all__'
class AreaSerializer(serializers.ModelSerializer):
class Meta:
model = Area
fields = '__all__'
class KeywordSerializer(serializers.ModelSerializer):
class Meta:
model = Keyword
fields = '__all__'
class PersonDocumentSerializer(DocumentSerializer):
class Meta:
document = PersonDocument
fields = [
'person_hash_index',
'surname',
'url',
'location',
'city',
'last_position',
'current_company',
'event',
'event_date',
'total_duration',
'updated',
]
Views.py
from django.db.models import Q
from rest_framework.response import Response
from rest_framework.views import APIView
from .serializers import *
from rest_framework import viewsets
from django_elasticsearch_dsl_drf.constants import (
LOOKUP_QUERY_GTE,
LOOKUP_QUERY_LTE,
LOOKUP_QUERY_IN,
LOOKUP_QUERY_CONTAINS,
LOOKUP_FILTER_RANGE,
SUGGESTER_COMPLETION
)
from django_elasticsearch_dsl_drf.filter_backends import (
FilteringFilterBackend,
SearchFilterBackend, # CompoundSearchFilterBackend should to use cause SearchFilterBackend is deprecated
SuggesterFilterBackend
)
from django_elasticsearch_dsl_drf.viewsets import DocumentViewSet
from .models import *
from .documents import PersonDocument
class PersonViewSet(viewsets.ModelViewSet):
serializer_class = PersonSerializer
queryset = Person.objects.all()
class ExperienceViewSet(viewsets.ModelViewSet):
serializer_class = ExperienceSerializer
queryset = Experience.objects.all()
class EducationViewSet(viewsets.ModelViewSet):
serializer_class = EducationSerializer
queryset = Education.objects.all()
class KeywordViewSet(viewsets.ModelViewSet):
serializer_class = KeywordSerializer
queryset = Keyword.objects.all()
class AreaViewSet(viewsets.ModelViewSet):
serializer_class = AreaSerializer
queryset = Area.objects.all()
class PersonSearch(APIView):
serializer_class = PersonSerializer
document_class = PersonDocument
def generate_q_expression(self, query):
return Q('match', name={'query': query})
def get(self, request, query):
q = self.generate_q_expression(query)
search = self.document_class.search().query(q)
return Response(self.serializer_class(search.to_queryset(), many=True).data)
class PersonDocumentViewSet(DocumentViewSet):
document = PersonDocument
serializer_class = PersonDocumentSerializer
filter_backends = (FilteringFilterBackend, SearchFilterBackend, SuggesterFilterBackend) # CompoundSearchFilterBackend should to use cause SearchFilterBackend is deprecated
search_fields = ('first_name',)
filter_fields = {
'location': {
'field': 'location',
'lookups': [LOOKUP_QUERY_IN] # ?location__in=russia, ?location__in=russia__germany
},
'city': {
'field': 'city',
'lookups': [LOOKUP_QUERY_IN]
},
'last_position': {
'field': 'last_position',
'lookups': [LOOKUP_QUERY_IN]
},
'current_company': {
'field': 'current_company',
'lookups': [LOOKUP_QUERY_IN]
},
'event': {
'field': 'event',
'lookups': [LOOKUP_QUERY_IN]
},
'total_duration': {
'field': 'total_duration',
'lookups': [LOOKUP_QUERY_GTE, LOOKUP_QUERY_LTE, LOOKUP_FILTER_RANGE] # total_duration_gte=5, total_duration_lte=5, total_duration_range=5__7
}
}
suggester_fields = {
'first_name_suggest': {
'field': 'first_name.suggest',
'suggesters': [SUGGESTER_COMPLETION]
}
}
Фронтеенд на React, так же использую Elasticsearch, как поисковой движок
У тебя прописан аргумент related_name в обоих моделях. Через него и обращаешься к связанным записям.
Из документации:
The name to use for the relation from the related object back to this one. It’s also the default value for related_query_name (the name to use for the reverse filter name from the target model). See the related objects documentation for a full explanation and example. Note that you must set this value when defining relations on abstract models; and when you do so some special syntax is available.
If you’d prefer Django not to create a backwards relation, set related_name to '+' or end it with '+'. For example, this will ensure that the User model won’t have a backwards relation to this model:
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='+',
)
Вам поможет переписать serializer подобным образом:
class EducationSerializer(serializers.ModelSerializer):
class Meta:
model = Education
fields = '__all__'
class ExperienceSerializer(serializers.ModelSerializer):
class Meta:
model = Experience
fields = '__all__'
class PersonSerializer(serializers.ModelSerializer):
experience = serializers.SerializerMethodField()
education = serializers.SerializerMethodField()
class Meta:
model = Person
fields = [
...
'experience',
'education',
]
def get_experience(self, obj):
queryset = Experience.objects.filter(person=obj)
return [ExperienceSerializer(q).data for q in queryset]
def get_education(self, obj):
queryset = Education.objects.filter(person=obj)
return [EducationSerializer(q).data for q in queryset]