Retrieve details from multiple tables using django query - python

I need to write a single django query such that I'm able to display the "status, date, time, job_name, sol_name and dept_name"
so that a rest api is created.
Model classes
class Job_Report(models.Model):
id:models.BigAutoField(primary_key=True)
job_id: models.ForeignKey(Job, models.DO_NOTHING)
status:models.CharField(max_length=7)
date: models.DateField()
time: models.TimeField()
duration = models.BigIntegerField(blank=True, null=True)
class Job(models.Model):
id:models.BigAutoField(primary_key=True)
name:models.CharField(max_length=500)
folder_id:models.ForeignKey(Job_folder, models.DO_NOTHING)
class Job_folder(models.Model):
id:models.BigAutoField(primary_key=True)
repo_id:models.ForeignKey(Sol_folder, models.DO_NOTHING)
class Sol_folder(models.Model):
id:models.BigAutoField(primary_key=True)
sol_id:models.ForeignKey(Solution, models.DO_NOTHING)
class Solution(models.Model):
id:models.BigAutoField(primary_key=True)
name:models.CharField(max_length=500)
dep_id:models.ForeignKey(Department, models.DO_NOTHING)
class Department(models.Model):
id:models.BigAutoField(primary_key=True)
dept_name:models.CharField(max_length=500)
I tried using
query_set=Job_Folder.objects.raw('select Job_Folder.status,Job_Folder.date,Job_Folder.time,Job.name,Department.name,Solution.name from Job_Folder,Job,Solution')`
but resulted in error.
Any help would be appreciated.

If you are using Django Rest Framework, then it is easy to define the computed values in serializer:
No need to make a query in SQL
Just follow the steps:
Create Serializer of Job_Report model.
class Job_ReportSerializer(serializers.ModelSerializer):
job = serializers.SerializerMethodField()
sol = serializers.SerializerMethodField()
dept = serializers.SerializerMethodField()
class Meta:
model = Job_Report
fields = '__all__'
def get_job(self, obj):
# 'get_' + 'attribute-name' to give definition
return obj.job.job_name
def get_sol(self, obj):
return obj.job.folder.sol.sol_name
.
.
.
# Define for all SerializerMethodField
Follow this for more info about DRF (Django REST Framework) https://www.django-rest-framework.org/tutorial/quickstart/
You'll take hardly 2 hours to complete the tutorial, but trust me it'll save your days of work.
Let me know if you want a solution without DRF.

models.py
All your models as you defined,
Just make one change:
- Do not use ids. Just use foreign keys.
class Job_Report(models.Model):
job: models.ForeignKey(Job, models.DO_NOTHING)
status:models.CharField(max_length=7)
date: models.DateField()
time: models.TimeField()
duration = models.BigIntegerField(blank=True, null=True)
class Job(models.Model):
name:models.CharField(max_length=500)
folder:models.ForeignKey(Job_folder, models.DO_NOTHING)
class Job_folder(models.Model):
name:models.CharField()
repo:models.ForeignKey(Sol_folder, models.DO_NOTHING)
class Sol_folder(models.Model):
name:models.CharField(max_length=500)
sol:models.ForeignKey(Solution, models.DO_NOTHING)
class Solution(models.Model):
name:models.CharField(max_length=500)
dept:models.ForeignKey(Department, models.DO_NOTHING)
class Department(models.Model):
name:models.CharField(max_length=500)
Serializers.py
class Job_ReportSerializer(serializers.ModelSerializer):
job = serializers.SerializerMethodField()
sol = serializers.SerializerMethodField()
dept = serializers.SerializerMethodField()
class Meta:
model = Job_Report
fields = '__all__'
def get_job(self, obj):
# 'get_' + 'attribute-name' to give definition
return obj.job.name
def get_sol(self, obj):
return obj.job.folder.sol.name
.
.
.
# Define for all SerializerMethodField
views.py
#api_view(['GET'])
def list_reports(request):
job_reports = Job_Report.objects.all()
data = JobReportSerializer(job_reports, many=True, context={'request':request}).data
return Response(data)
#api_view(['GET'])
def detail_report(request, pk):
job_report = Job_Report.objects.get(id=pk)
data = JobReportSerializer(job_report,context={'request':request}).data
return Response(data)
urls.py
Add Urls to the functional views
Boom!!! API is ready, don't forget to add 'rest_framework' in INSTALLED_APPS in settings.py

Related

Django REST Framework, Serializers: Additional data?

Good day,
I would like to ask, if there's a possibility to gain additional data inside my serializers?
These are my models...
models.py
class Chair(models.Model):
name = models.CharField(max_length=100, null=False, blank=False, unique=True)
bookable = models.BooleanField(default=False)
user_created = models.CharField(max_length=100)
date_created = models.DateField(auto_now_add=True)
class Booking(models.Model):
chair = models.ForeignKey(Chair, on_delete=models.CASCADE)
day = models.DateField()
user_name = models.CharField(max_length=100)
user_created = models.CharField(max_length=100)
date_created = models.DateField(auto_now_add=True)
and these my serializers...
serializers.py
class BookingSerializer(serializers.ModelSerializer):
class Meta:
model = Booking
fields = '__all__'
class ChairSerializer(serializers.ModelSerializer):
class Meta:
model = Chair
fields = '__all__'
When making a request inside js like this...
views.py
#api_view(['GET'])
def bookings_by_date(request, pk):
bookings = Booking.objects.filter(day=pk)
serializer = BookingSerializer(bookings, many=True)
return Response(serializer.data)
script.js
let url = '...here's my url for Booking...';
fetch(url)
.then((resp) => resp.json())
.then(function(data) {
// do something here
});
...I would like to get not only the id of the Chair (models.Foreignkey), but also it's name. My first thought was doing something like this...
class ChairSerializer(serializers.ModelSerializer):
class Meta:
model = Chair
fields = [
...
'chair',
'chair__name',
...
]
...but this doesn't seem to work! Does anyone know a solution for my problem? Thanks for all your help and have a great weekend!
You can use one of this two ways:
1-) Using SerializerMethodField. You can add readonly fields with this way. You should add get_<field_name> method or give a method name that you want to run for this field with name keyword. You can look the document for more details.
class BookingSerializer(serializers.ModelSerializer):
chair__name = serializers.SerializerMethodField()
class Meta:
model = Booking
fields = '__all__'
def get_chair_name(self, obj):
return obj.chair.name
2-) Using CharField with source attribute:
You can define basically this field fill from where.
class BookingSerializer(serializers.ModelSerializer):
chair__name = serializers.CharField(source='chair__name')
class Meta:
model = Booking
fields = '__all__'

'Workout' object has no attribute 'exercises'

I just started to play with Django trying to make an API REST, for some reason that i don't understand i'm getting this error 'Workout' object has no attribute 'exercises' when i do GET - /workouts.
I'm using Django REST framework to make this thing.
This is part of my code that I think you need to help me:
serializers.py
class WorkoutSerializer(serializers.ModelSerializer):
exercises = serializers.StringRelatedField(many=True)
class Meta:
model = Workout
fields = ['id', 'name', 'creation_date', 'exercises']
class ExerciseSerializer(serializers.ModelSerializer):
class Meta:
model = Exercise
fields = ['url', 'video_url', 'workout']
models.py
class Workout(models.Model):
name = models.CharField(max_length=30)
creation_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.name
class Exercise(models.Model):
video_url = models.URLField(max_length=200)
workout = models.ForeignKey(Workout, related_name='workout', on_delete=models.CASCADE)
def __str__(self):
return str(self.video_url)
views.py
class WorkoutViewSet(viewsets.ModelViewSet):
queryset = Workout.objects.all()
serializer_class = WorkoutSerializer
class ExerciseViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows exercises to be viewed or edited.
"""
queryset = Exercise.objects.all()
serializer_class = ExerciseSerializer

check if object in ManyToMany field django rest framework

here is my models.py:
class Post(models.Model):
id = models.AutoField(primary_key=True)
body = models.TextField(max_length=10000)
date = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
liked_by = models.ManyToManyField(User, blank=True, related_name='liked_by')
class Meta:
ordering = ['-date']
serializers.py:
class PostSerializer(serializers.ModelSerializer):
user = UserSerializers()
total_likes = serializers.SerializerMethodField()
total_comments = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ('id','body','date','user','total_likes','total_comments')
def get_total_likes(self, instance):
return instance.liked_by.count()
def get_total_comments(self, instance):
return instance.comment_set.count()
Q1: how do i check if a user exists in ManyToManyField of a single post?
Q2: shouldn't i use ManyToManyField in drf? then which would be better?
I don't have enough reps to comment, but if you have a post instance and a user instance, you could do something like:
post.liked_by.filter(id=user.id).exists()
Does that help you or are you asking where you should be implementing this? e.g. in your view or serializer etc...

How to access an extra field in an m2m intermediary table from my admin model?

I have two models Tender and Status joined in a M2M relationship by a bridge table TenderStatus. The bridge table has an extra date field in it. I'm using an inline for the admin form. But I want to be able to display the date from the intermediary table in the admin view. I can access the name of the Status, so I can easily show a comma-separated list of statuses for example, but I cannot figure out how to access the date field. I've tried using a callable in the admin class to no avail (I think I just don't know how to access the date field from the callable). I also created a custom str function for TenderStatus, but it doesn't seem to pay attention to that. Here's the code I have so far:
models.py
class Tender(models.Model):
name = models.CharField(max_length=255)
tender_status = models.ManyToManyField(
'Status',
through='TenderStatus',
related_name='tender_status'
)
class Meta:
managed = False
db_table = 'tender'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def __str__(self):
return self.name
class TenderStatus(models.Model):
tender = models.ForeignKey('Tender', on_delete=models.DO_NOTHING, blank=True, null=False)
status = models.ForeignKey('Status', on_delete=models.DO_NOTHING, null=False,)
date = models.DateField(blank=True, null=False)
def __str__(self):
return str(self.date) + ': ' + str(self.status)
class Meta:
managed = False
db_table = 'tender_status'
class Status(models.Model):
status = models.CharField(max_length=50)
short_name = models.CharField(max_length=5)
class Meta:
managed = False
db_table = 'status'
def __str__(self):
return str(self.status)
admin.py
class TenderAdmin(admin.ModelAdmin, ExportCsvMixin):
list_display = ['name', 'status_list']
def status_list(self, obj):
# I BELIEVE THIS IS THE ELEMENT I NEED HELP WITH, HOW TO CALL THE DATE HERE
# THE FOLLOWING DOES NOT WORK
return [str(x.date) + ': ' + x.status for x in obj.tender_status.all()]
class Meta:
model = 'Tender'
Note: My app does not use custom views, it only uses the admin models. I do not have the option to add custom views, as the organization will not permit it.
Edit: To clarify, I'm looking for a way to list out the statuses + dates in a string formatted way in the list_display, using only what is available in django-admin.

Object of type 'ListSerializer' is not JSON serializable

I want to get all customer data and responses and also remarks.
This is model.py
class Customer(models.Model):
name = models.CharField(max_length=200)
email_address = models.CharField(max_length=200)
phone_number = models.CharField(max_length=20)
age = models.SmallIntegerField(default=14)
remarks = models.ManyToManyField(Remark,null=True,blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.id)
class Response(models.Model):
question = models.ForeignKey(Question)
customer = models.ForeignKey(Customer)
response_text = models.CharField(max_length=100, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField()
def __str__(self):
return str(self.id)
This is serializers.py
class ResponseSerializer(ModelSerializer):
class Meta:
model = Response
fields = '__all__'
class RemarksSerializer(ModelSerializer):
class Meta:
model = Remark
fields = '__all__'
class CustomerInformationSerializer(ModelSerializer):
remarks = RemarksSerializer(many=True)
responses = serializers.SerializerMethodField()
def get_responses(self, obj):
responses = Response.objects.filter(customer=obj)
return ResponseSerializer(responses, many=True)
class Meta:
model = Customer
fields = ('name', 'email_address', 'phone_number', 'age', 'remarks', 'responses')
This is services.py
def customer_information(company_id=1):
cus = Customer.objects.filter(remarks__company_id=company_id)
return CustomerInformationSerializer(cus, many=True).data
This is views.py
class CustomerInformationView(APIView):
def get(self, request):
company_id = request.GET.get('company_id', 1)
resp = {'data': customer_information(company_id)}
return Response(data=resp, status=status.HTTP_200_OK)
This is url.py
url(r'^customer/$', CustomerInformationView.as_view()),
I'm having this problem. How can I solve this. Kindly guide me.
get function in your view should return responses.data, insted of responsed.
SIDE NOTE
First, let me point you to a resource that I think is GREAT for anything dealing with Django REST Framework:
Classy Django REST Framework. It is a fantastic resource because you can easily dig right into the source code to see how you may or may not need to override default operations.
MY ANSWER
What I suggest is that instead of using the APIView, you use ListAPIView.
It would look something like this:
from rest_framework.generics import ListAPIView
class Customer(models.Model):
name = models.CharField(max_length=200)
email_address = models.CharField(max_length=200)
phone_number = models.CharField(max_length=20)
age = models.SmallIntegerField(default=14)
remarks = models.ManyToManyField(Remark,null=True,blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.id)
class Response(models.Model):
question = models.ForeignKey(Question)
customer = models.ForeignKey(Customer, related_name='responses')
response_text = models.CharField(max_length=100, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField()
def __str__(self):
return str(self.id)
class ResponseSerializer(ModelSerializer):
class Meta:
model = Response
fields = '__all__'
class RemarksSerializer(ModelSerializer):
class Meta:
model = Remark
fields = '__all__'
class CustomerInformationSerializer(ModelSerializer):
remarks = RemarksSerializer(many=True)
responses = ResponseSerializer(many=True)
class Meta:
model = Customer
fields = ('name', 'email_address', 'phone_number', 'age', 'remarks', 'responses')
class CustomerInformationView(ListAPIView):
queryset = Customer.objects.all()
serializer_class = CustomerInformationSerializer
lookup_field = 'remarks__company'
Note the change that I made by adding related_name to the customer field on your Response model. See Django documentation for more information on related_name. In short, it adds responses as a field name on your Customer model so that you can travel backwards through that relationship.
This is not tested, but this should be a better strategy to do what you want without having to have a get_responses method, or a services.py.
Some there might be error because of missing "/" at the end of path like "event-api"=incorrect and "event-api/" correct. That worked for me. Hope you also have same problem.
Incorrect: path('event-api',views.event_view,name="event-view")
Correct: path('event-api/',views.event_view,name="event-view")

Categories