Django find duplicates in two tables - django

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.

Related

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) How to connect urls-views-models in ManyToMany, OneToMany relationship

I made some models which have ManyToMany, OneToMany relationships, and then I tried to make appropriate class in views.py, so that one can see sub models related to the chosen model.
But in terms of connecting models-serializers-views-urls, I just couldn't figure out how to make it work...
So, what I want to do is : (simplified)
There are 3 models.
Party
People
Food
So Party has ManyToMany relationship with People, and OneToMany relationship with Food. When I reached url like /party_id/people_id, then I want to get specific person's information from given party id.
Here goes my code.
models.py
class Party(models.Model):
par_id = models.TextField()
par_people = models.ManyToManyField(People)
class People(models.Model):
peo_id = models.TextField()
peo_name = models.TextField()
peo_type = models.TextField()
class Food(models.Model):
foo_id = models.TextField()
foo_party = models.ForeignKey(Party, on_delete=models.CASCADE)
serializers.py
class PartySerializer(serializers.ModelSerializer):
class Meta:
model = Party
fields = ('par_id', 'par_people')
# People, Food has same structure...
views.py
class PartyList(generics.ListAPIView):
queryset = Party.objects.all()
serializer_class = PartySerializer
# People, Food has same structure...
urls.py
Here's the part where I got lost
#redundancy reduced...(e.g. import)
urlpatterns = [
path('party/<int:par_id>/<int:peo_id>', views.PartyList.as_view()),
path('party/<int:par_id>/<int:foo_id>', views.PartyList.as_view()),
]
So If I reach website/party/1/3, I want to see person's information(whose peo_id is 3) of party(whose par_id is 1). For food, It goes the same.
Should I make new class in views.py to make it work? But how can url check par_id and foo_id at the same time if I use PartyList view class..? Any help would be much appreciated.
I think something like this should work. The basic principle if work out if using peo_id or foo_id and then filter the queryset on that basis.
def get (self, *args, **kwargs):
id = kwargs.get(peo_id, None)
if id:
self.queryset.filter(par_people__peo_id=id)
else:
id = kwargs.get(foo_id, None)
self.queryset.filter(foo_party=id)

Django's prefetch_related and select_related on more complex relationships in Admin

I have a somewhat complex relationship between multiple models. A simplified example:
class Country(models.Model):
name = models.CharField([...])
[...]
def __ str__(self):
return f'{self.name}'
class Region(models.Model):
country = models.ForeignKey(Country)
name = models.CharField([...])
[...]
def __ str__(self):
return f'{self.name}'
class CityManager(models.Manager):
def get_queryset(self):
return super().get_queryset().select_related('region', 'region__country')
class City(models.Model):
name = models.CharField([...])
region = models.ForeignKey(Region)
objects = CityManager()
def __str__(self):
return f'{self.region.country} - {self.region} - {self.name}'
Hence when I want to display some kind of list of cities (e.g. list all cities in Germany), I have to use select_related to be even remotely efficient otherwise I query for Country each time the __str__ is called. This is not the problem.
The problem is that when I have unrelated group of models and I want to FK to City, such as:
class Tour(models.Model):
[...]
class TourItem(models.Model):
tour = models.ForeignKey(Tour)
city = models.ForeignKey(City)
[...]
Tour would represent a planned tour for some music band; and TourItem would be a specific tour in a given city. I have a simple admin interface for this, so that TourItem is an inline field for the Tour (ie. so multiple tour items can be edited/added simultaneously). The problem is that now there are multiple queries firing for same Country when looking up the City FK and I'm not sure how to solve it. I tried what follows, but it did not work as expected:
class TourManager(models.Manager):
def get_queryset(self):
return super().get_queryset().prefetch_related('touritem_set__city', 'touritem_set__city__region', 'touritem_set__city__region__country')
And neither did this work:
class TourItemManager(models.Manager):
def get_queryset(self):
return super().get_queryset().select_related('city', 'city__region', 'city__region__country')
How can I adjust the managers/models so that when I load Tour's admin there will not be additional queries fired for Country?
You can use ModelAdmin select_related to select related tables
list_select_related = ('author', 'category')
If that is not fully helpful and you still want to do override try with following in your custom Manager
def get_queryset(self, request):
return super(TourItemManager,self).queryset(request).select_related('city', 'city__region', 'city__region__country')

django many to many admin shows all and not associated items

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

django views - accessing a m2m field in a generic view

I've stumbled upon this issue and my noob brain got fried trying to resolve it. I feel like there's some basic concepts here that I'm missing.
So I have this "Films" model with category choice field and a m2m relationship to a "Directors" model, and I'm trying to write 2 different views, one that returns a list of films filtered by category and one that returns a list of films filtered by director.
The first one is easy, but I just don't know how to get the director model's name field to create the second filter.
So I have this models (i've taken the irrelevant stuff out including the category thing i mentioned above)
class Director(models.Model):
name = models.CharField(max_length=50)
web = models.URLField(blank=True, help_text= "opcional")
class Film(models.Model):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length= 15)
director = models.ManyToManyField(Director, blank=True, help_text= "opcional")
this url
(r'^peliculas/director/(?P<director>\w+)/$', 'filtered_by_director'),
and this view
def filtered_by_director(request,director):
return list_detail.object_list(
request,
queryset = Film.objects.filter(director.name=director),
template_name ='sections/film_list.html',
template_object_name = 'film',
paginate_by = 3
)
The same template is supposed to be used by both views to render the relevant list of objects
The view doesn't like the filter i'm using at the queryset for the m2m field, but I have no clue how to do it really, I've tried whatever I could think of and it gives me a "keyword can't be an expression" error
Any help to this lowly noob will be appreciated.
Line queryset = Film.objects.filter(director.name=director),
needs to read: queryset = Film.objects.filter(director__name=director),
Field lookups are done by __ double underscore syntax:
http://docs.djangoproject.com/en/dev/topics/db/queries/#field-lookups
In your filter, try specifying the director name like (documentation):
filter(director__name=director)