How to add multiple foreign key in Django - python

I am beginner in Django.
I am having two models
class Employee(models.Model):
full_name = models.CharField(max_length=120,default=None)
designation = models.CharField(max_length=80,default=None)
class Leave(models.Model):
employee = models.ForeignKey(Employee, related_name='employee')
number_of_days = models.IntegerField(default=0)
Now I have inline Leave Model with Employee in admin.py
so that I can add as many leaves I want in employees
But when I retrieve this model in views new Leaves are created, all I want is one employee should show total = number of days, but it creates new leaves, not able to build any logic here I am stuck, please ask if u don't understand what I am asking.

Not exactly sure what you are asking, but my guess is you want to display the total number of absent days of an employee in the admin. You can use aggregation and Sum in particular and a custom method on your model:
# models
from django.db.models import Sum
class Employee(models.Model):
def absent_days(self):
return self.leaves.aggregate(s=Sum('number_of_days'))['s'] or 0
absent_days.short_description = 'Absent days' # used as column header/field label
class Leave(models.Model):
# note the changed related name!
employee = models.ForeignKey(Employee, related_name='leaves')
# admin
class EmployeeAdmin(admin.ModelAdmin):
readonly_fields = [..., 'absent_days', ...]
fields = [..., 'absent_days', ...]
list_display = [..., 'absent_days', ...]

Related

Django: get only objects with max foreignkey count

The question is quite simple but possibly unsolvable with Django.
For example I have a model
class MyModel(models.Model)
field_a = models.IntegerField()
field_b = models.CharField()
field_c = models.ForegnKey(MyOtherModel)
The question is how to select only objects that have a maximal count of relations with MyOtherModel and preferably(almost mandatory) with only a single query set?
Lets say, we have 100 entries all together, 50 pcs. point to field_c_id=1, 40 pcs. to field_c_id=2 and rest 10 pcs. entries to field_c_id = 3.
I need only those which point to field_c_id=1? as 50 would be maximal count.
Thanks...
Ok first you need a related_name on your MyModel model
in models.py
class MyOtherModel(models.Model)
field_a = models.IntegerField()
class MyModel(models.Model)
field_a = models.IntegerField()
field_b = models.CharField()
field_c = models.ForeignKey(MyOtherModel, on_delete=models.CASCADE, related_name="my_other_model")
Than you can get most used MyOtherModel
in views.py
most_used = MyOtherModel.objects.annotate(my_other_model_count=Count('my_other_model')).order_by('-my_other_model_count')[:1]
put [:1] if you need 1 if you need more you can set any quantity or remove it
Or
And here is another solution which is better than this
First we have to add one field to your MyOtherModel which keeps count of MyModel
class MyOtherModel(models.Model)
field_a = models.IntegerField()
my_model_count = models.IntegerField()
And we have to update count when you add, update or delete object to MyModel for that I recommend to use django signals
in your models.py
from django.dispatch import receiver
from django.db.models.signals import pre_save, pre_delete
#at the bottom of your model
#receiver(pre_save, sender=MyModel)
def products_reputation_count(sender, instance, *args, **kwargs):
if instance.pk:
old_instance = MyOtherModel.objects.get(id=instance.pk)
old_instance.reputation.product_count -= 1
instance.reputation.product_count += 1
else:
instance.reputation.product_count += 1
#receiver(pre_save, sender= MyModel)
def products_reputation_count(sender, instance, *args, **kwargs):
instance.reputation.product_count += 1
and in your views.py
most_used = MyOtherModel.objects.order_by("-my_model_count")[:1]
I think this would be helpful. If you have any question related this answer feel free to ask more. Have a good day.

How to provide "add" option to a field in django admin?

In django admin i had one model named 'student', here it has three fields, NAME, ROLL NO. & SUBJECT.
in models.py:
from django.db import models
class Student(models.Model):
Name = models.CharField(max_length=255, blank = False)
Roll_No = models.CharField(max_length=10, blank = False)
Subject = models.CharField(max_length=20, blank = False)
def __str__(self):
return self.Name
now i want this SUBJECT field to be Dynamic, like there will be "+" sign besides the SUBJECT field, by clicking that one more SUBJECT field will be added right after it and so on, but maximum 10 SUBJECT fields can be added like that.
You can update the existing value when doing the subject addition from views.
Like for example in views:
student = Student.objects.get(id=1)
student.Subject += "New_Subject_Name"
student.save() # this will update only
Add other conditions/check for the existing subject and check for 10 subjects accordingly.

Django Models design: many-to-many relationship with specific needs

I am in the process of designing a couple of new models for my django app and this is what I need:
class Bookmaker(models.Model):
name = models.CharField(max_length=50)
accepted_countries = ?
restricted_countries = ?
class Country(models.Model):
name = models.CharField(max_length=50)
bookmakers = ?
So I need a model Bookmaker and a model Country and they need to be related, BUT every bookmaker should have a list of countries that are accepted and a list of countries that are excluded.
The question marks are both in the Country and in the Bookmaker models as I'm not sure which way the relation should go.
Not sure if I'm on the right path, but I'm thinking that I need a couple of many-to-many relationships.. And, which is the right way (if any) to use the same Country model (those will be different instances) in both accepted_countries and restricted_countries?
Thanks.
You should use two many to many relations with related_name to separate them:
class Bookmaker(models.Model):
name = models.CharField(max_length=50)
accepted_countries = models.ManyToManyField(Country, related_name="accepted_for")
restricted_countries = models.ManyToManyField(Country, related_name="restricted_for")
You can then use the reverse relation as:
bookmakers_for_which_county_is_accepted = Country.objects.values('accepted_for').distinct()
bookmakers_for_which_county_is_restricted = Country.objects.values('restricted_for').distinct()
docs
You need indeed some many to many:
class Bookmaker(models.Model):
name = models.CharField(max_length=50)
accepted_countries = models.ManyToMany('Country',related_name='accepted')
restricted_countries = models.ManyToMany('Country', related_name= 'restricted')
class Country(models.Model):
name = models.CharField(max_length=50)
bookmakers = models.ManyToMany(Bookmaker)
Then if you create a form to edit a bookmaker you will be abble to add the name, the accepted and restricted countries:
forms.py
class BookmakerForm(models.ModelForm):
class Meta:
model = Bookmaker
fields = ['name', 'accepted_countries', 'restricted_countries']
#for having a better default display you can change the widget:
self __init__(self, *args, **kwargs):
super(BookmakerForm, self).__init__(*args, **kwargs)
self.fields['accepted_countries'].widget = CheckboxSelectMultiple()
self.fields['restricted_countries'].widget = CheckboxSelectMultiple()
In the view by default if you just need to check if form.is_valid() and save. django modelform will manage the intermediary step for you.
The bookmakers field in Country allows you to retrieve all the bookmaker associated to a country without having to check in both accepted and restricted (it's more for convenience).
But you will need to add the bookmaker to that list on you view like:
class CreateBookmakerView(CreateView):
model = Bookmaker
form_class = BookmakerForm
success_url = reverse_lazy('your success url name here')
def form_valid(self,form):
bookmaker = form.save()
bookmaker.country_set.add(bookmaker)
bookmaker.save()
return HttpResponseRedirect(self.get_success_url())

Linear sum of foreign key count django

I have a profile class in my model like follow:
class UserProfile(models.Model):
first_name = ....
....
I two other classes that has foreign key to profile model:
class A(models.Model):
profile = models.ForeignKey(UserProfile)
....
class B(models.Model):
profile = models.ForeignKey(UserProfile)
Now I want to filter active users. I want something like follows. But I don't know how to do it in django!
UserProfile.objects.filter((2*count(A) + count(B))__gte=10).all()
You need to use annotate on the queryset first to run the calculation, then you can filter by it.
from django.db.models import Count
UserProfile.objects.annotate(
score=2*Count('a') + Count('b')
).filter(score__gte=10)

Searching by related fields in django admin

I've been looking at the docs for search_fields in django admin in the attempt to allow searching of related fields.
So, here are some of my models.
# models.py
class Team(models.Model):
name = models.CharField(max_length=255)
class AgeGroup(models.Model):
group = models.CharField(max_length=255)
class Runner(models.Model):
"""
Model for the runner holding a course record.
"""
name = models.CharField(max_length=100)
agegroup = models.ForeignKey(AgeGroup)
team = models.ForeignKey(Team, blank=True, null=True)
class Result(models.Model):
"""
Model for the results of records.
"""
runner = models.ForeignKey(Runner)
year = models.IntegerField(_("Year"))
time = models.CharField(_("Time"), max_length=8)
class YearRecord(models.Model):
"""
Model for storing the course records of a year.
"""
result = models.ForeignKey(Result)
year = models.IntegerField()
What I'd like is for the YearRecord admin to be able to search for the team which a runner belongs to. However as soon as I attempt to add the Runner FK relationship to the search fields I get an error on searches; TypeError: Related Field got invalid lookup: icontains
So, here is the admin setup where I'd like to be able to search through the relationships. I'm sure this matches the docs, but am I misunderstanding something here? Can this be resolved & the result__runner be extended to the team field of the Runner model?
# admin.py
class YearRecordAdmin(admin.ModelAdmin):
model = YearRecord
list_display = ('result', 'get_agegroup', 'get_team', 'year')
search_fields = ['result__runner', 'year']
def get_team(self, obj):
return obj.result.runner.team
get_team.short_description = _("Team")
def get_agegroup(self, obj):
return obj.result.runner.agegroup
get_agegroup.short_description = _("Age group")
The documentation reads:
These fields should be some kind of text field, such as CharField or TextField.
so you should use 'result__runner__team__name'.

Categories