Using Django linked model to save additional information in at runtime - django

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?

Related

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 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.

limit_choices_to in DjangoAdmin

One of my models contains a ForeignKey-field to a model that has multiple thousand instances.
When I display a record, all of these are loaded into a dropdown, which I a) don't need and b) is slow as frack, especially when displaying multiple records on one page.
Page size shoots up to multiples of 3.5mb because of the size of the dropdown.
I thought about using "limit_choices_to" to contain that, but
country = models.IntegerField(blank=True, null=True)
location = models.ForeignKey(Geonames, limit_choices_to = {'cowcode': country}, related_name='events')
does not work.
Is there even a way to do that?
Update:
What do I want to display?
I want to show all places (Geonames) that are in the country of the EventRecord that the code above is taken from. I want to show only these places, not the whole list of all possible places.
Why don't I need all places?
a) Page load times: 3.5 minutes for a page load is a tad too long
b) See above: An Event takes place in a certain country, so I don't need to show locations that are not in that country
What you want is to make limit_choices_to aware to your instance, which is not possible.
What you should do is set the queryset property of location field in your admin form, something similar to this:
class EventRecordAdminForm(forms.ModelForm):
class Meta:
model = EventRecord
def __init__(self, *args, **kwargs):
super(EventRecordAdminForm, self).__init__(*args, **kwargs)
self.fields['location'].queryset = Geonames.objects.filter(cowcode=self.instance.country)
and of course use that form for your admin:
class EventRecordAdmin(admin.ModelAdmin):
form = EventRecordAdminForm
See here for docs
HTH!
if you are using admin interface you can use raw_id_fields in ModelAdmin:
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'
ordering = ('-publication_date',)
filter_horizontal = ('authors',)
raw_id_fields = ('publisher',)
from Django Book:
sometimes you don’t want to incur the overhead of having to select all the related objects to display in the drop-down. For example, if our book database grows to include thousands of publishers, the “Add book” form could take a while to load, because it would have to load every publisher for display in the box.
The way to fix this is to use an option called raw_id_fields. Set this to a tuple of ForeignKey field names, and those fields will be displayed in the admin with a simple text input box () instead of a select.
Not sure why that is not working for you. But I think a better solution would be to use django-smart-selects. That way you can have the user choose country first. Then the Geoname dropdown is only populated when the user first chooses country.
From the docs:
If you have the following model:
class Location(models.Model)
continent = models.ForeignKey(Continent)
country = models.ForeignKey(Country)
area = models.ForeignKey(Area)
city = models.CharField(max_length=50)
street = models.CharField(max_length=100)
And you want that if you select a continent only the countries are available that are located on this continent and the same for areas you can do the following:
from smart_selects.db_fields import ChainedForeignKey
class Location(models.Model)
continent = models.ForeignKey(Continent)
country = ChainedForeignKey(
Country,
chained_field="continent",
chained_model_field="continent",
show_all=False,
auto_choose=True
)
area = ChainedForeignKey(Area, chained_field="country", chained_model_field="country")
city = models.CharField(max_length=50)
street = models.CharField(max_length=100)
This example asumes that the Country Model has a continent = ForeignKey(Continent) field and that the Area model has country = ForeignKey(Country) field.

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