Get count for objects - python

I want to get specialization count for all the doctors and want to show it in the admin panel
Here are the models
class Doctor(models.Model):
name = models.CharField(max_length=1300)
specialization = models.ForeignKey(Specialization)
clinic = models.ForeignKey(Clinic)
class Specialization(models.Model):
name = models.CharField(max_length=30)
For instance, I want to show like:
Dermatologist: 10
Ophthalmologist: 15
I'm very new to Django and have no clue on how to go about it.

Don't use obj.doctor_set.count() recipe. This method will be called for each Specialization record.
Use single SQL query with aggregation:
# admin.py
from django.contrib import admin
from django.db.models import Count
from app.models import Specialization
class SpecializationAdmin(admin.ModelAdmin):
list_display = ['name', 'doctor_count_display']
def get_queryset(self, request):
qs = super(SpecializationAdmin, self).get_queryset(request)
return qs.annotate(doctor_count=Count('doctor')).order_by('-doctor_count')
def doctor_count_display(self, obj):
return obj.doctor_count
doctor_count_display.short_description = 'Number of doctors'
doctor_count_display.admin_order_field = 'doctor_count'
admin.site.register(Specialization, SpecializationAdmin)

you can accomplish this in this way for example:
#admin.py
class SpecializationAdmin(admin.ModelAdmin):
list_display = ('get_count',)
def get_count(self, obj):
return u"{}: {}".format(obj.name, obj.doctor_set.count())
get_count.short_description = "Number of Doctors"
and register this like:
# also in admin.py
admin.site.register(Specialization, SpecializationAdmin)
dont forget to import Specialization model into admin.py

You need to understand about queries Django Queries
To show the number of dermatologist:
Doctor.objects.filter(specialization__name="Dermatologist").count()
To show the number of ophthalmologist
Doctor.objects.filter(specialization__name="Ophthalmologist").count()

Related

Trying to use objects from an entirely different model using a foreign key

So I want to assign a 'Rank' to specific users with a Foreign key (I created a model for Ranks with other objects such as an image, description, etc).
What I want to do is to use those objects from the Rank Model and bring them over to my template. So that when I assign the rank "Newbie" to a specific user, it will show all the objects from the Rank like image, and descriptions.
Can anyone help?
Thanks
Here's my views.py
from .models import user as UserModel
def index(request):
return render(request, 'pages/index.html')
def user(request, user_id):
profile = get_object_or_404(UserModel, pk=user_id)
context = {
'profile' : profile,
}
return render(request, 'user/user.html', context)
Here is the model for Ranks
from django.db import models
class Ranks(models.Model):
rank_name = models.CharField(max_length=300)
photo = models.ImageField(upload_to='photos/&Y/%m/%d/')
description = models.TextField(blank=True)
rank_points = models.IntegerField()
rank_promotionss = models.IntegerField()
def __str__(self):
return self.rank_name
It's rather simple - add ForeignKey field to your user model:
class UserModel(models.Model):
...
rank = models.ForeignKey(Ranks, on_delete=models.CASCADE,...)
and then use it in your template simply by calling e.g. profile.rank.rank_name
1 little optimization tip, in your user() method, add select_related column:
def user(request, user_id):
profile = get_object_or_404(UserModel.objects.select_related('rank'), pk=user_id)
or it would perform additional queries to fetch the Rank
Another tip, keep your models names in singular, e.g. Rank instead of Ranks

Django admin: SORTING list_display field from reverse FK aggregation

The model Station has a FK to System. I want to display Station count as a column in the System admin change list. I have achieved this with the code below but I am having difficulty making the 'num_stations' column sortable.
model.py:
class System(models.Model):
pass
class Station(models.Model):
system_info = models.ForeignKey(System)
admin.py:
class SystemAdmin(admin.ModelAdmin):
def num_stations(self, obj):
return obj.station_set.count()
# num_stations.admin_order_field = ????
num_stations.short_description = 'stations'
list_display = (num_stations',)
The ordinary 'station_set' syntax for a reverse relationship doesn't seem to work in the admin_order_field, even though other stack overflow question commonly show traversing a relation in the forward direction is supported
e.g.
Django admin: how to sort by one of the custom list_display fields that has no database field
Can “list_display” in a Django ModelAdmin display attributes of ForeignKey fields?.
Thanks to Alasdair for their comment, the answer was in this question as pointed out. Here is the solution that fits my problem:
class SystemAdmin(admin.ModelAdmin):
def num_stations(self, obj):
return obj.num_stations
def get_queryset(self, request):
# def queryset(self, request): # For Django <1.6
qs = super(SystemAdmin, self).get_queryset(request)
# qs = super(CustomerAdmin, self).queryset(request) # For Django <1.6
qs = qs.annotate(num_stations=Count('station'))
return qs
num_stations.admin_order_field = 'num_stations'
num_stations.short_description = 'stations'
list_display = ('num_stations', )

Django filter by multipleChoiceField

I have model Skill
class Skill(models.Model):
hero= models.ForeignKey(Hero)
name = models.CharField(max_length=255)
And I have model Hero
class Hero(models.Model):
name = models.CharField(max_length=255)
I use multiple choice field to select skills
OPTIONS = (
("sharingan", "sharingan"),
("rasengan", "rasengan"),
("fireball", "fireball"),
)
skills= forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple(),
choices=OPTIONS)
I use get request to send my form and my search page url becomes something like:
mysite.com/search?skills=shiringan&skills=rasengan
In my views.py I have
def vip(request):
heroes = Hero.objects.all
return render(request, 'app_name/search.html',{'heroes': heroes})
What should I write in views.py to select all heroes with chosen skills?
You probably want to use ManyToManyField Instead.
like this:
# models.py
from django.db import models
class Skill(models.Model):
name = models.CharField(max_length=255)
class Hero(models.Model):
name = models.CharField(max_length=255)
skills = models.ManyToManyField(Skill)
and use forms like this:
# forms.py
from django import forms
from .models import Hero
class HeroForm(forms.ModelForm):
class Meta:
model = Hero
fields = '__all__'
and finally your views:
# views.py
from .models import Hero, Skill
from .forms import HeroForm
def my_heroes(request):
heroes = Hero.objects.all()
return render(request, 'your_template.html', {'heroes':heroes})
def hero_skill_select(request):
if request.method == 'POST':
form = HeroForm(request.POST)
# do something
else:
form = HeroForm()
return render(request, 'template_with_form.html', {'form':form})
Shouldn't the Hero model have Skill as foreignfield, rather than the opposite? I don't understand the logic behind your classes.
Based on your current Classes and their relationship, you could do something like this:
chosen_skills = Skill.objects.values('hero').distinct().annotate(skill='name', hero_name='hero__name')
Haven't tested it, but basically, for every distinct hero, it returns the skill and the name. Let me know if this helps.

Populate Django ManyToManyField Options Based on Other Field in Admin

I am having problems filtering options for a ManyToManyField on the Django Admin Add screen based on input to another field on the same form. I am new to Django and have been unable to use any of the generic fixes described elsewhere because they are all slightly different than my situation. Here is my situation:
I have three models in my project: Class, Student, and AttendanceRecord. In the Django Admin, when adding an attendance record, I would like to change the options for the field Absent_Students based on the selection made for the field Associated_Class. So, for example, if Associated_Class "CS 450" is selected, the options for Absent_Students should change to only students whose class_list includes CS 450.
Here are my models:
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import User
from django.utils.encoding import python_2_unicode_compatible
import random, string
# Create your models here.
#This is the model for a student
#python_2_unicode_compatible
class Student(models.Model):
pass
Student_First_Name = models.CharField(max_length=200)
Student_Last_Name = models.CharField(max_length=200)
Student_ID_Number = models.CharField(max_length=200)
Student_Class = models.ForeignKey('Class', null=True)
def __str__(self):
return self.Student_Last_Name + ',' + self.Student_First_Name
# This is the model for a class
#python_2_unicode_compatible
class Class(models.Model):
class Meta:
verbose_name_plural = "Classes"
Class_Name = models.CharField(max_length=200)
Student_List = models.ManyToManyField('Student', related_name='class_list')
Professor = models.ForeignKey(User,null=True)
AddCode = models.IntegerField
pass
def __str__(self):
return self.Class_Name
def getName(self):
return self.Class_Name
def getProfessor(self):
return self.Professor.id
def getProf(self):
return self.Professor
def getStudents(self):
return self.Student_List
#This is the model for attendance records
class AttendanceRecord(models.Model):
class Meta:
verbose_name = "Attendance Record"
Associated_Class = models.ForeignKey(Class, on_delete=models.CASCADE, related_name='Attendance_Records')
Date = models.DateField()
Absent_Students = models.ManyToManyField('Student', blank=True)
Present_Students = models.ManyToManyField('Student', related_name='a')
def get_associated_class_id(self):
return self.Associated_Class
def __str__(self):
return self.Associated_Class.__str__() + ' on date ' + self.Date.__str__(self)
I have tried doing this by editing the AttendanceRecordAdminForm class and AttendanceRecordAdmin class. My problem is that when setting the self.fields['Absent_Students].queryset I do not know how to access the currently selected Associated_Class on the form. I keep getting an error that "AttendanceRecord has no Associated_Class". Here are those classes just discussed in their entirety:
class AttendanceRecordAdminForm(forms.ModelForm):
class Meta:
model = AttendanceRecord
fields = '__all__'
def __init__(self, *args, **kwargs):
super(AttendanceRecordAdminForm, self).__init__(*args, **kwargs)
instance = kwargs.get('instance', None)
self.fields['Absent_Students'].queryset = Student.objects.filter(class_list__id=self.instance.get_associated_class_id())
self.fields['Present_Students'].queryset = Student.objects.filter(class_list__id=1)
class AttendanceRecordAdmin(admin.ModelAdmin):
form = AttendanceRecordAdminForm
filter_horizontal = ('Absent_Students', 'Present_Students',)
Basically, I am looking for a way to access the currently entered Associated_Class on the admin form so I can properly filter the queryset.
After hours more of online searching I finally found what I needed. A chained ManyToMany from the smart_select app makes this very easy. This link: How to use django-smart-select describes the install process and also links to the documentation for using it once it is installed. Hopefully this helps some others as well.

Filtering in django rest framework

In my project I use django rest framework. To filter the results I use django_filters backend.
There is my code:
models.py
from django.db import models
class Region(models.Model):
name = models.CharField(max_length=100, blank=True, null=False)
class Town(models.Model):
region = models.ForeignKey(Region)
name = models.CharField(max_length=100, blank=True, null=False')
filters.py
import django_filters
from models import Town
class TownFilter(django_filters.FilterSet):
region = django_filters.CharFilter(name="region__name", lookup_type="contains")
town = django_filters.CharFilter(name="name", lookup_type="contains")
class Meta:
model = Town
fields = ['region', 'town']
views.py
from models import Town
from rest_framework import generics
from serializers import TownSerializer
from filters import TownFilter
class TownList(generics.ListAPIView):
queryset = Town.objects.all()
serializer_class = TownSerializer
filter_class = TownFilter
So, I can write ?region=Region_name&town=Town_name to the end of the request url, and the result will be filtered.
But I want to use only one get param in the request url, which can have region or town name as value. For example ?search=Region_name and ?search=Town_name. How can I do this?
There are a few options, but the easiest way is to just override 'get_queryset' in your API view.
Example from the docs adapted to your use case:
class TownList(generics.ListAPIView):
queryset = Town.objects.all()
serializer_class = TownSerializer
filter_class = TownFilter(generics.ListAPIView)
serializer_class = PurchaseSerializer
def get_queryset(self):
queryset = Town.objects.all()
search_param = self.request.QUERY_PARAMS.get('search', None)
if search_param is not None:
"""
set queryset here or use your TownFilter
"""
return queryset
Another way is to set your search_fields on the list api view class in combination use the SearchFilter class. The problem is that if you're filtering over multiple models, you may have to do some additional implementation here to make sure it's looking at exactly what you want. If you're not doing anything fancy, just put double underscores for region for example: region__name
With dj-rest-filters, you can write your filters with similar syntax as of serializer. For your case, it will be like this
from djfilters import filters
class MyFilter(filters.Filter):
search = filters.CharField()
def filter_search(self, qs, value):
qs = qs.filter(#Your filter logic here)
return qs

Categories

Resources