django many to many admin shows all and not associated items - django

I have an object structure that looks like so:
Customer -- one to many -- Locations
Locations -- many to many -- Departments
Departments -- one to many -- Objects
here is my models.py (my admin.py is standard):
class Customer(models.Model):
customerName = models.CharField(max_length=64)
class Department(models.Model):
departmentName = models.CharField(max_length=64)
class Location(models.Model):
customer = models.ForeignKey(Customer)
departments = models.ManyToManyField(Department)
class Object(models.Model):
location = models.ForeignKey(Location)
department = models.ForeignKey(Department)
The problem is that when I want to set the department for objects I get every department in the django admin drop down. I even get the departments that are associate with locations of different customers.
Also, when I am setting the department of an object, I get the same list of all available departments, even those associated with different customers.
How can I have the drop down only show me the departments that a customer supports?

A quick one line solution to filter down a many to many relationship is put this line in your admin object:
filter_horizontal = ('departments',)

You can provide your own form with filtered queryset
class DepartmentAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DepartmentAdminForm, self).__init__(*args, **kwargs)
self.fields['customers'].queryset = Customer.objects.filter(...)
class Meta:
model = Department
class DepartmentAdmin(admin.ModelAdmin):
form = DepartmentAdminForm

I believe the answer is to use the formfield_for_manytomany
https://docs.djangoproject.com/en/1.4/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_foreignkey

Related

Using Django linked model to save additional information in at runtime

I am trying to create a scenario where I may add a value of level (to add the degree of escalation) for the saved instances of a model "Person" according to some criteria (say seniority or position of responsibility). The form is intended to look something like this (the values in column Level to be filled at runtime):
Person
Level
Franc Correa
2
Liz Burnett
3
Christian Amarilo
3
Mark Tannit
1
The Level values will be entered at runtime to the (already) created persons.
I have the following models:
class Person(models.Model):
name = models.CharField(max_length=128)
designation = models.CharField(max_length=128)
class Level(models.Model):
level = models.PositiveSmallIntegerField()
person = models.ForeignKey(Person,...)
As already stated above, I want to have all values of the field person in the form so that I may enter the level values at runtime.
My form is:
class levelForm(forms.ModelForm):
model = Level
fields = ('person', 'level')
def __init__(self, *args, **kwargs):
super (levelForm, self).__init__(*args,**kwargs)
self.fields['person'].queryset = Person.objects.all()
However, what I am getting is the person names as a dropdown field and not all names at the same time (in successive rows).
Is there a way to resove this?

Django find duplicates in two tables

I am working on a database of public figures and their positions. I have the following models:
class Person(models.Model):
full_name = models.CharField()
appointments = models.ManyToManyField('Appointment', through='PersonsNAppointments')
class Appointment(models.Model):
position = models.CharField()
persons = models.ManyToManyField('Person', through='PersonsNAppointments')
Names are not unique. So in situation when I have John Donne(id_1) and John Donne(id_2) and "John Donne" got appointed they both would get this appointment. I want to create an admin panel where only people with same names and same appointments are shown so the admin could handle these duplicates manually. I need help with writing get_queryset.
So far I've figured out how to get duplicated names
dup_names = Persons.objects.values('full_name')\
.annotate(name_count=Count('id'))\
.filter(name_count__gt=1).values('full_name')
dup_objects = Persons.objects.filter(full_name__in=dupes_names)
How to get only those persons who share same appointments?
you can do it in one query:
class MyDoublePersonsModelAdmin(ModelAdmin):
model = Person
def get_queryset(self, *args, **kwargs):
quesryset = super().get_queryset(*args, **kwargs)
return queryset.annotate(name_count=Count('appointments__persons__id').filter(appointments__persons__name = F('name'), name_count__gt = 1)
annotate adds count by person hwo get appointment.
filter get only annotated person with duplicated names.
filter get only appointments where more than one person with duplicated name.

Django ListView Through Model Relationships

I have been reading through here and I can't seem to find the answer of the question I am looking for. Maybe I'm not asking the correct question.
I have three models.
Recruiter
Office
Recruit
Recruiters are assigned to multiple offices and each office has multiple employees. What I need to be able to do is create a listview that lists all of the employees that are associated with a recruiter. So something like this:
Recruiter 1 has Office 1 and 2 assigned to them. Office 1 has Employee 1,2,3. Office 2 has employee 3,4,5
The Listview should display all employees under Recruiter 1.
My Models are:
class Recruit(models.Model):
id = models.CharField(max_length=25,primary_key=True, unique=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
mobile_phone = models.CharField(max_length=20,null=True)
preferred_phone = models.CharField(max_length=20,null=True)
email= models.CharField(max_length=100,null=True)
officeid = models.ForeignKey(Office,on_delete=models.DO_NOTHING,related_name="recruit")
class Office(models.Model):
office_name = models.CharField(max_length=100)
officeid = models.CharField(max_length=20,primary_key=True,unique=True)
class Recruiter(models.Model):
user = models.OneToOneField(User, related_name='recruiter',on_delete=models.CASCADE)
organization = models.ForeignKey(UserProfile,on_delete=models.CASCADE)
Do I need a AssingedOffices table to join this all together?
class AssignedOffice(models.Model):
user = models.ForeignKey(UserProfile,on_delete=models.CASCADE)
esa_office = models.ForeignKey(Office,on_delete=models.CASCADE)
Just not sure how to connect them & display in the listview.
Edit Added start of view.
class MyESA(LoginRequiredMixin,ListView):
template_name = "recruits/recruits_list.html"
user= User.objects.select_related('recruiter')
paginate_by = 20
model=Recruit
context_object_name = 'recruits'
def get_queryset(self):
queryset = Recruit.objects.filter(probablity=1)
return queryset
If you want to achieve a Many-to-one relationship like this
Recruiters are assigned to multiple offices
then you should have a ForeignKey in the Office model not the other way around. I presume that each Office has one Recruiter. Otherwise you should have a Many-to-Many relationship that is constructed with a ManyToManyField
I presume that by
each office has multiple employees
You meant that each Office object has multiple Recruit objects
If you want to list all of the Recruit objects in a ListView for model Recruiter then I think the easiest way would be to add a ForeignKey field in the Recruit model and refer to it via related object reference

Django Model Forms with Multi-Level Foreign Keys

I need to create a model form in Django and have the following arbitrary scenario.
Real Estates
============
ID
...some extra fields...
CityID
Cities
======
ID
Name
Region
======
ID
Name
Country
=======
ID
Name
What I would like to do is to let user choose the Country first, then Region and lastly the City. (Populate the child category with javascript after user selects the parent category.) However, I don't want to add the 'Region' and 'Country' fields to the 'Real Estate' table. The order of the fields are also important, that is, 1) Country, 2) Region and 3) City.
Can you suggest any approach to this? Thanks!
I'm assuming you are trying to show the user a limited set of options for "Region" after he selected a country and a limited set of options for "City" after he selected the region so as to provide a way to sensibly select a city for the real estate instead of having to pick something from a long list of random cities?
You could specify additional fields on the RealEstate ModelForm that aren't actual fields on the RealEstate model and provide logic in the form's .__init__() and if needed also in .clean() and .save() methods to take care of the additional fields. Perhaps something like this:
class Country(models.Model):
name = models.CharField(...)
class Region(models.Model):
name = models.CharField(...)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
class City(models.Model):
name = models.CharField(...)
region = models.ForeignKey(Region, on_delete=models.CASCADE)
class RealEstate(models.Model):
name = models.CharField(...)
city = models.ForeignKey(City, on_delete=models.CASCADE)
class RealEstateForm(forms.ModelForm):
country = forms.ChoiceField(required=False)
region = forms.ChoiceField(required=False)
class Meta:
model = RealEstate
fields = ['name', 'city', 'country', 'region']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['country'].choices = Country.objects.all().values('id', 'name)
self.fields['region'].choices = Region.objects.all().values('id', 'name', 'country__id')
self.fields['city'].choices = City.objects.all().values('id', 'name', 'region__id', 'region__country__id')
Providing choices that include the referenced region ID and country ID for each city will allow you to filter these in the front end if you manage to use these choices as the user is selecting country and region without having to make a separate backend request to get the filtered list. Of course, you would end up with a larger response being sent this way in one go - if your data set size is very large (how many countries, how many regions, how many cities) this may not be wanted.

Dynamic Drop List for Many to Many relation

I have 2 tables "Client" and "Location".
class Client(models.Model):
name = models.CharField(max_length=50)
class Location(models.Model):
name = models.CharField(max_length=50)
A Client can be in many Locations and a Location can have many Clients.
I created a third table to hold this relationship:
class Client_Location(models.Model):
client = models.ForeignKey(Client, on_delete=models.CASCADE)
location = models.ForeignKey(Location, on_delete=models.CASCADE)
I created a form to test whether i can make the dropdownlist dynamic, so if i were to pick a client, any location linked to that client would only appear.
class ClientLocationForm(forms.ModelForm):
class Meta:
model = Client_Location
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['location'].queryset = Location.objects.none()
So far i was only able to make the location field blank. Not sure where to go next as examples i've seen aren't exactly like mine.
With minimal changes to your code, you may add the existing Client_Location model as through model for a new explicit ManyToMany relationship. To do so, add the following field to your Client model:
locations = models.ManyToManyField('Location', through='Client_Location', related_name='clients')
If you need fully dynamic updates, you need to write a view which provides a list of locations for a specified client (client.locations.all()) and then show them on your page.