Django admin limit model from foreign key - python

I have the following model setup:
The problem is that when I try to pull the object up in the admin page, computer_names links to several hundred thousand rows that aren't relevant and the page never loads. How can I filter computer_names to only the user selected objects for the ManyToMany field?
class ScoringException(models.Model):
class Meta:
ordering = ['date_modified']
requester = models.CharField('Requester',max_length=50,null=False,blank=False)
computer_names = models.ManyToManyField(Computer)
domain = models.ForeignKey(Domain)
exception_kpi_types = models.ManyToManyField(ScoringType)
expiration_date = models.DateField('Expiration Date')
reason = models.CharField('Reason',max_length=1000,null=False,blank=False)
approved = models.BooleanField('Approved')
date_modified = models.DateTimeField('Date Updated',auto_now=True)

You can use raw_id_fields in the admin so that Django doesn't render the hundred thousand rows of data:
#admin.register(ScoringException)
class ScoringExceptionAdmin(admin.ModelAdmin):
....
raw_id_fields = ['computer_names']
With raw_id_fields, Django will display the list of ids for selected m2m objects. A search button is also added to make adding new objects for the m2m relationship easier.
See the documentation for more information.

Related

Filtering django queryset by a value of the through table of a m2m relationship

I'm trying to do this:
queryset.filter(m2m_related_lookup__through_table_field=value)
These are the models, simplified:
class User(models.Model):
name = models.CharField("nom", max_length=100)
surname = models.CharField("cognoms", max_length=100)
class Activity(models.Model):
name = models.CharField("Títol", max_length=200)
date_start = models.DateField("Dia inici")
date_end = models.DateField("Dia finalització")
enrolled = models.ManyToManyField(User, related_name='enrolled_activities', through='ActivityEnrolled')
class ActivityEnrolled(models.Model):
class Meta:
db_table = 'main_activity_personetes_enrolled'
activity = models.ForeignKey(Activity, on_delete=models.CASCADE)
user = models.ForeignKey(Personeta, on_delete=models.CASCADE)
date_enrolled = models.DateTimeField("data d'inscripció")
confirmed = models.BooleanField("confirmada", default=False)
I guess is quite simple, just a many 2 many with a custom through table, so I can store the enrollment date and some other things there.
This relationship is set at Activity, with a related_name of 'enrolled_activities'.
So, how can I query "all the users where the ActivityEnrolled.enrollment_date is in 2019" using Django's ORM?
This is for a custom filter (with admin.SimpleListFilter) for a change_list view in the Django Admin, which is listing the User items. In other words, is like I'm doing User.objects.filter(blabla).
Trying: queryset.filter(enrolled_activities__date_enrolled__year=2019) obviously throws the error Related Field got invalid lookup: date_enrolled, because enrolled_activities does not refer to the through table but to the related table (this is: Activity), and this field does not exist there.
Is the only solution to query the through table instead of Users?
Like: ActivityEnrolled.objects.filter(date_enrolled__year=2019) + grouping the results so it only returns one row per each User. I know I can do that but it's quite nasty, I've been trying to find a cleaner way to avoid it but with no success.
Thank you very much!!
So, how can I query "all the users where the ActivityEnrolled.enrollment_date is in 2019" using Django's ORM?
A many-to-many relation is in fact just a combination of two one-to-many tables. We thus can filter on the one-to-many relation with:
User.objects.filter(activityenrolled__enrollment_date__year=2019).distinct()
The .distinct() will prevent yielding the same user, if th user has multiple activities for which he/she was enrolled in 2019.

Django M2M with addititional data with through

My problem is as follows. I am saving data for patients from a form on a webpage. The form is generated from model definitions in models.py. The information that I save is name, surname amongst others. I have a field for diagnosis which is selected using a multichoiceField and I save it using manytomany.
When the data is saved, a separate table is created for the diagnosis assigned to each patient as expected. The table contains a diagnosis and the ID of the patient it applies to. Each diagnosis is saved as a separate record.
In addition to selecting the diagnosis, I also save the date that the diagnosis is made. You will see what I mean in the models.py and form.py code below.
I would like to have the date for which the diagnosis was made also saved in the table but I can't figure out how to do this. I have tried following the docs: https://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany as well as some other posts on SO, but cannot figure out how to do it. I can't figure out how the views, forms and models need to be set up in order to achieve. Is it possible to do this and if so how? I have tried using an intermediate model with manytomany and 'through', but I do not understand it. Any help with this would be greatly appreciated.
Below is a simplified version of my code:
models.py:
class diagnosisChoices(models.Model): #This represents the list in the drop down menu for the different diagnosis.
diagnosis = models.CharField(max_length = 50)
def __str__(self):
return self.diagnosis
class PatientData(models.Model):
Name = models.CharField(max_length=100)
Surname = models.CharField(max_length=100)
dateOfBirth = models.DateField(default = datetime.datetime.now())
diagnosis = models.ManyToManyField(
'diagnosisChoices',
#on_delete=models.CASCADE,
)
views.py:
def patientDataView(request):
uId = request.user.id
if request.method == "POST":
form = PatientDataForm(request.POST)
if form.is_valid():
model_instance = form.save(commit=False)
model_instance.timestamp = timezone.now()
model_instance.save()
#model_instance.add(uId)
form.save_m2m()
return HttpResponseRedirect('/dataBase')
else:
form = PatientDataForm()
return render(request, "dataBaseTest.html", {'form': form})
date_of_diagnosis = models.DateField(default=datetime.datetime.now())
forms.py
from django.forms import ModelForm
from django import forms
from .models import PatientData
from .models import diagnosisChoices #This is the list of diagnosis in the dropdown
from django.forms import extras
import datetime
from functools import partial
class PatientDataForm(ModelForm):
class Meta:
now = datetime.datetime.now()
thisYear = now.year
DateInput = partial(forms.DateInput, {'class': 'datepicker'})
widgets = {
}
model = PatientData
fields = ['Name',
'Surname',
'dateOfBirth',
'diagnosis',
'date_of_diagnosis',
]
Thanks,
Thomas
The main thing that you are not getting is on the models.py, so I will focus on it.
You need three tables to do what you have described: diagnosisData, PatientData and a 'membership' table which I call diagnosisPatient. Then you build your model like this:
class diagnosisChoices(models.Model):
diagnosis = models.CharField(max_length = 50)
class PatientData(models.Model):
Name = models.CharField(max_length=100)
Surname = models.CharField(max_length=100)
dateOfBirth = models.DateField(default = datetime.datetime.now())
diagnosis = models.ManyToManyField('diagnosisChoices',through='diagnosisPatient')
class diagnosisPatient(models.Model):
patient = models.ForeignKey('PatientData')
diagnosis = models.ForeignKey('diagnosisChoices')
dateOfDiagnosis = models.DateField()
Once you have your model built this way, you should save your PatientData and your diagnosisChoices instances as usual. FOr the many to many relation, you should save it manualy on the diagnosisPatient table using the apropriate foreign keys and date. You can query the many to many relation from the PatientData model as usual with objects.all() function.
The thing here to keep in mind is that ManyToMany relations in django are always creating a new membership table for you behind the scenes. So when you do not need to insert extra information on the relationship the diagnosisPatient table is just made of two foreign keys, and it is hidden. The through argument on this relationship is just bringing this table to light and giving you control back to put whatever new relationship you like.

Django admin search and edit foreign fields

I've got a two part question regarding Django Admin.
Firstly, I've got a Django model Classified that has a foreign key field from another table Address. On setting data, I've got no issues with any of the fields and all fields get saved correctly.
However, if I want to edit the foreign field in the entry in Classified, it doesn't display the old/existing data in the fields. Instead it shows empty fields in the popup that opens.
How do I get the fields to display the existing data on clicking the + so that I can edit the correct information?
Secondly, I'm sure I've seen search fields in Django Admin. Am I mistaken? Is there a way for me to implement search in the admin panel? I have over 2 million records which need to be updated deleted from time to time. It's very cumbersome to manually go through all the pages in the admin and delete or edit those.
Adding Model Code:
Classified
class Classified(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=256)
contact_person = models.CharField(max_length=300, blank=True)
email = models.CharField(max_length=100, blank=True)
address = models.ForeignKey(Address)
subcategory = models.ForeignKey(Subcategory)
Address
class Address(models.Model):
id = models.AutoField(primary_key=True)
build_add = models.CharField(max_length=255)
street_add = models.CharField(max_length=255)
area = models.CharField(max_length=255)
city = models.ForeignKey(Cities)
The + means just that - add a new instance of the related object and relate the object you're editing to that. Because you're adding a new object it will be blank to start. If you want to be able to edit existing related objects from another object's admin you need to use inlines.
In your app's admin.py have something like:
from django.contrib import admin
from yourapp.models import Address, Classified
class AddressInline(admin.TabularInline):
model = Address
class ClassifiedAdmin(admin.ModelAdmin):
inlines = [AddressInline,]
admin.site.register(Classified, ClassifiedAdmin)
Adding search from there is really easy.
...
class ClassifiedAdmin(admin.ModelAdmin):
inlines = [AddressInline,]
search_fields = [
'field_you_want_to_search',
'another_field',
'address__field_on_relation',
]
...
Note the double underscore in that last one. That means you can search based on values in related objects' fields.
EDIT: This answer is right in that your foreignkey relationship is the wrong way round to do it this way - with the models shown in your question Classified would be the inline and Address the primary model.

Django - complex forms with multiple models

Django 1.1
models.py:
class Property(models.Model):
name = models.CharField()
addr = models.CharField()
phone = models.CharField()
etc....
class PropertyComment(models.Model):
user = models.ForeignKey(User)
prop = models.ForeignKey(Property)
text = models.TextField()
etc...
I have a form which needs to display several entries from my Property model each with a corresponding PropertyComment form to collect a user's comments on that property. In other words, allowing a User to comment on multiple Property instances on the same page.
This seems outside the intended usage of an Inline formset since it is multi-model to multi-model vs. single-model to multi-model. It seems like trying to iterate through the Property instances and create an inline formset for each is not only clunky, but I'm not even sure it could work.
Any ideas on where to start on this?
Have you thought about using the comment framework:
http://docs.djangoproject.com/en/dev/ref/contrib/comments/
If that doesnt work for you then maybe look into inlineformset_factory:
http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets
from django.forms.models import inlineformset_factory
PropertyCommentFormSet = inlineformset_factory(Property, PropertyComment)
property= Property.objects.get(name=u'some property name')
formset = PropertyCommentFormSet(instance=property)
etc...

Django admin choice field dynamically populated by generic foreign key's model fields

Say I have the following simple models for some tagging application (this is simplified from the actual code):
# Model of tag templates
class TagTemplate(models.Model):
name = models.CharField()
content_type = models.ForeignKey(ContentType)
class Tag(models.Model):
template = models.ForeignKey(TagTemplate)
object_id = models.PositiveIntegerField()
* content_object = generic.GenericForeignKey('template__content_type', 'object_id')
# Each tag may display the
class TagTemplateItemDisplay(models.Model):
template = models.ForeignKey(TagTemplate)
content_type_field = models.CharField()
font_size = models.IntegerField()
I have two questions:
1) In the line marked with the *, I understand from the documentation that I need to pass the two field names as per the contenttype framework. In my case the content_type field is specified within the template model. I'd like to avoind a duplicate content_type field within the 'tag' model to get the GenericForeignKey working. Is this possible? Or do I need some custom manager to implement a duplicate content_type within the tag model?
2) I'd like to use the admin site with these models. Is it possible to dynamically create a choice dropdown for the 'content_type_field' field where the contents corresponds to a list of fields from the chosen content_type of the parent model (ie. tagTemplate) when using Tabularinline layout?
eg. in the admin site I pick a model (content_type field) for a new tagTemplate record that contains the fields ('name', 'age', 'dob'), I'd like the TabularInline forms to dynamically update the 'content_type_field' to contain the choices name, age and dob. If i then pick a different model in the parent tagTemplate content_type field, the choices in the child tagTemplateItemDisplay content_type_field of the inline are updated again.
You can subclass the form for that model
class TagTemplateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(TagTemplateForm, self).__init__(*args, **kwargs)
if self.instance.content_type == SomeContentType:
**dynamically create your fields here**
elif self.instance.content_type == SomeOtherContentType:
**dynamically create your other fields here**
Then in your TagAdmin model you need to have:
form = TagTemplateForm
to override the default form created for the admin site.
Not a complete solution but should get you started.
For the dynamic form generation, you might start by reading over this

Categories

Resources