How to add autocomplete for foreign key using autocomplete_fields - django

I have following model
from django.db import models
class Ipaddress(models.Model):
ipaddress=models.CharField(max_length=20)
slug = models.SlugField(unique=True)
machinename=models.CharField(max_length=500)
user=models.CharField(max_length=200)
department= models.ForeignKey('Department',on_delete=models.CASCADE,default='Empty')
location= models.ForeignKey('Location', on_delete=models.CASCADE)
updated = models.DateField("Date Updated",null=True)
note =models.TextField()
def __str__(self):
return self.ipaddress[:50]
In the admin page:
from django.contrib import admin
from pages.models import Post, Device, DeviceType, DeviceModel, Ipaddress, DeviceGroup, Location,Department,Comment
from django_admin_listfilter_dropdown.filters import DropdownFilter, RelatedDropdownFilter
class IpaddressAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('ipaddress',)}
search_fields = ['ipaddress', ]
list_display = ('ipaddress', 'machinename', 'user', 'department','location','updated',)
list_filter = (
('user', DropdownFilter),
('department', RelatedDropdownFilter),
('location', RelatedDropdownFilter),
)
When I try to add a device it shows following page:
The location of the list could be a few thousand racks. So, i need to type the rack instead of scrolling 1000 of records. Any idea how i can do that.

Until someone shares a complex solution i suggest you add your ForeignKey Location to your search_fields instead like:
search_fields = ['foreign_key__related_fieldname']
So if your "location names" are defined with the field 'title' in your Location model you could do:
from django.contrib import admin
from .models import Ipaddress
class IpaddressAdmin(admin.ModelAdmin):
search_fields = ['ipaddress', 'location__title']
admin.site.register(Ipaddress, IpaddressAdmin)
Note: stripped it down to the essential in this example that assumes this is in app/admin.py and that the Ipadress model is in app/models.py
This is really convenient since django admin breaks down multiple search words (seperated by spaces) so you can search for "ip location" like:
127.0.0.1 Sweden
And it would search both your 'ipadress' field and your ForeignKey 'Location'.
Pros:
Fast! No need to reload the view twice if you need to search after applying filter!
Simple
Cons:
Possibly slower querying if one adds many foreign keys for search_fields or has a huge amount of objects in the foreign key model.
Django 2.1 docs: ModelAdmin.search_fields

Related

New attributes are not showing up in django admin dashboard

Previously I was using my project with sqlite. Then started a new project copied the data from previous project and made some changes, and I'm using this with mysql.
This is my models.py(not full)
from django.db import models
from django.db.models import CheckConstraint, Q, F
class College(models.Model):
CITY_CHOICES=[('BAN','Bangalore')]
id=models.IntegerField(primary_key=True)
name=models.CharField(max_length=50)
city=models.CharField(choices=CITY_CHOICES,default='BAN',max_length=10)
fest_nos=models.IntegerField()
image=models.ImageField(default='default.jpg',upload_to='college_pics')
class Meta():
db_table='college'
def __str__(self):
return self.name
class Organizer(models.Model):
id=models.IntegerField(primary_key=True)
name=models.CharField(max_length=25)
phone=models.IntegerField()
def __str__(self):
return self.name
class Fest(models.Model):
FEST_CHOICES=[
('CUL','Cultural'),
('TEC','Technical'),
('COL','College'),
('SPO','Sports'),
]
id=models.IntegerField(primary_key=True)
name=models.CharField(max_length=50)
clg_id=models.ForeignKey(College,on_delete=models.CASCADE)
fest_type=models.CharField(choices=FEST_CHOICES,default='COL',max_length=10)
fest_desc=models.TextField(default='This is a fest')
#below two field are not showing up in admin page
start_date=models.DateField(auto_now_add=True)
end_date=models.DateField(auto_now_add=True)
event_nos=models.IntegerField()
org_id=models.ManyToManyField(Organizer)
image=models.ImageField(default='default.jpg',upload_to='fest_pics')
class Meta:
constraints = [
CheckConstraint(
check = Q(end_date__gte=F('start_date')),
name = 'check_start_date',
)
]
db_table='fest'
def __str__(self):
return self.name
The start_date and end_date attributes are the new ones added in this project. It was not there in the old one.
My admin.py file
from django.contrib import admin
from .models import College, Event, Fest, Organizer, Participated
admin.site.register(College)
admin.site.register(Organizer)
admin.site.register(Fest)
admin.site.register(Event)
admin.site.register(Participated)
But in my admin dashboard, while adding new fests I'm not getting the option to add start and end date.
I made migrations once again, fake migrated etc. What to do?
Is check constraint under model fest causing this problem?
They fields won't show up on Django Admin because they have auto_now_add=True so, the user shouldn't touch them.
You can make auto_now_add field display in admin by using readonly_fields in the admin class(this only show the data, you still can't edit it because it's auto_now_add)
#register with a class to use
class FestAdmin(admin.ModelAdmin):
readonly_fields = ('start_date', 'end_date')
admin.site.register(Fest, FestAdmin)

How to query by joining a Django Model with other, on a non unique column?

I have the following models in my models.py file in my django project
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.conf import settings
class CustomUser(AbstractUser):
pass
# add additional fields in here
class PDFForm(models.Model):
pdf_type=models.IntegerField(default=0)
pdf_name=models.CharField(max_length=100,default='')
file_path=models.FileField(default='')
class FormField(models.Model):
fk_pdf_id=models.ForeignKey('PDFForm', on_delete=models.CASCADE,default=0)
field_type=models.IntegerField(default=0)
field_page_number=models.IntegerField(default=0)
field_x=models.DecimalField(max_digits=6,decimal_places=2,default=0)
field_y=models.DecimalField(max_digits=6,decimal_places=2,default=0)
field_x_increment=models.DecimalField(max_digits=6,decimal_places=2,default=0)
class Meta:
ordering= ("field_page_number", "field_type")
class UserData(models.Model):
fk_user_id=models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,default=0)
field_type=models.IntegerField(default=0)
field_text=models.CharField(max_length=200,default='')
field_date=models.DateField()
Here is how the models are related
1) a pdfform contains a pdf form and path for it on the file system
2) A pdfform has multiple FormFields in it. Each field has attributes, and the specific one under discussion is field_type
3)The UserData model has user's data, so one User can have multiple rows in this table. This model also has the field_type column.
What I am trying to query is to find out all rows present in the Userdata Model which are present in the FormField Model ( matched with field_type) and that are of a specific PDFForm.
Given that the Many to Many relationship in django models cannot happen between no unique fields, how would one go about making a query like below
select a.*, b.* from FormField a, UserData b where b.fk_user_id=1 and a.fk_pdf_id=3 and a.field_type=b.field_type
I have been going through the documentation with a fine toothed comb, but obviously have been missing how django creates joins. what is the way to make the above sql statement happen, so I get the required dataset?
I think UserData is missing a relation to FormField, but if you had this relation you could do:
UserData.objects.filter(
fk_user_id=1, # Rename this to user, Django wilt automicly create a user_id column
form_field__in=FormField.objects.filter(
fk_pdf_id=<your pdfid> # same as fk_user_id
)
)
Edit updated models
When you use a ForeignKey you don't have to specify the _id or default=0, if you don't always want to fill the field its better to set null=True and blank=True
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.conf import settings
class CustomUser(AbstractUser):
pass
# add additional fields in here
class FieldTypeMixin:
TYPE_TEXT = 10
TYPE_DATE = 20
TYPE_CHOISES = [
(TYPE_TEXT, 'Text'),
(TYPE_DATE, 'Date'),
]
field_type=models.IntegerField(default=TYPE_TEXT, choises=TYPE_CHOISES)
class PDFForm(models.Model):
pdf_type = models.IntegerField(default=0)
pdf_name = models.CharField(max_length=100,default='')
file_path = models.FileField(default='')
class FormField(models.Model, FieldTypeMixin):
pdf_form = models.ForeignKey('PDFForm', on_delete=models.CASCADE)
field_page_number = models.IntegerField(default=0)
field_x = models.DecimalField(max_digits=6,decimal_places=2,default=0)
field_y = models.DecimalField(max_digits=6,decimal_places=2,default=0)
field_x_increment = models.DecimalField(max_digits=6,decimal_places=2,default=0)
class Meta:
ordering = ("field_page_number", "field_type")
class SubmittedForm(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE)
pdf_form = models.ForeignKey(PDFForm, models.CASCADE)
class SubmittedFormField(models.Model, FieldTypeMixin):
submitted_form = models.ForeignKey(SubmittedForm, models.CASCADE)
form_field = models.ForeignKey(FormField, models.CASCADE, related_name='fields')
field_text = models.CharField(max_length=200,default='')
field_date = models.DateField()
class Meta:
unique_together = [
['submitted_form', 'form_field']
]

How to display changelist of multiple models in django admin?

I need to display multiple models in django admin change list view. I want to use single search box to filter all of them at once. Is there an easy way to do it?
My idea was to inherit from admin site, add another view to it and iterate over models in modified change_list.html but i can't import models and ModelAdmins because i get django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet. error so i can't get the same context that django uses to render regular change_list.html.
What's the correct way to do it? Is there simpler approach?
As Ohad suggested, the most robust approach is probably to make formal relationships between the models from which you want the objects to display together. You have a couple of options here. Essentially you will want to make a master class and then subclass your models from it. This makes a lot of sense if your models are ontologically related to a parent concept. For example:
Publication
Book
Magazine issue
Books and magazines are both publications. They both share some fields, like title and publication date. But they differ in that a book usually has a single author and a magazine has volumes and issue dates. Django already provides a couple different approaches to subclassing using Model inheritance. However, after trying these myself I found that the django-polymorphic extension is way better. Here is a code example of a Django 3.0 app using django-polymorphic which has a Book model and a Magazine model with a single listing of all publications that shows all of the books and magazines in the system.
models.py
from django.db import models
from polymorphic.models import PolymorphicModel
class Publication(PolymorphicModel):
title = models.CharField(max_length=256)
publication_year = models.IntegerField()
class Book(Publication):
author_first = models.CharField(max_length=256)
author_last = models.CharField(max_length=256)
class Magazine(Publication):
volume_number = models.IntegerField()
issue_name = models.CharField(max_length=256)
admin.py
from django.contrib import admin
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin, PolymorphicChildModelFilter
from .models import Publication, Book, Magazine
class PublicationChildAdmin(PolymorphicChildModelAdmin):
""" Base admin class for all child models """
base_model = Publication # Optional, explicitly set here.
#admin.register(Book)
class BookAdmin(PublicationChildAdmin):
base_model = Book # Explicitly set here!
# show_in_index = True # makes child model admin visible in main admin site
list_display = ('title', 'publication_year', 'author_first', 'author_last')
#admin.register(Magazine)
class MagazineAdmin(PublicationChildAdmin):
base_model = Magazine # Explicitly set here!
# show_in_index = True # makes child model admin visible in main admin site
list_display = ('title', 'publication_year', 'issue_name')
#admin.register(Publication)
class PublicationParentAdmin(PolymorphicParentModelAdmin):
""" The parent model admin """
base_model = Publication # Optional, explicitly set here.
child_models = (Book, Magazine)
list_filter = (PolymorphicChildModelFilter,) # This is optional.
list_display = ('title', 'publication_year')
This will of course only display those fields that are common (in the Publication model). If you want to display fields that are particular to each model there are various tricks for this. Here's one quick way to do it:
admin.py
...
#admin.register(Publication)
class PublicationParentAdmin(PolymorphicParentModelAdmin):
""" The parent model admin """
base_model = Publication # Optional, explicitly set here.
child_models = (Book, Magazine)
list_filter = (PolymorphicChildModelFilter,) # This is optional.
list_display = ('title', 'publication_year', 'issue', 'author')
def author(self, obj):
if obj.polymorphic_ctype.model == 'book':
book = Book.objects.get(pk=obj.pk)
return book.author_first + ' ' + book.author_last
return None
def issue(self, obj):
if obj.polymorphic_ctype.model == 'magazine':
return str(Magazine.objects.get(pk=obj.pk).issue_name)
return None
Tada!
From the docs it seems that there is no easy solution.(if there is no relation between the models)
https://docs.djangoproject.com/en/2.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.search_fields
So if the search is commonly used build a special model/models that combines the data that might be searched

How to manage django admin for country section with same back and frontend listing page

I am new in Django. I am creating a app countries which will have listing page in both frontend and backend. I have manage frontend by writing code in root urls.py
path('countries/', include(('countries.urls', 'countries'), namespace = 'countries'))
And in countries/urls.py
path('', views.index, name='index'),
in models.py I write country model for frontend and In views.py write index function for frontend.
This is my models.py
class Country(models.Model):
iso_code = models.CharField(max_length=2, unique=True)
name = models.CharField(max_length=255, unique=True)
is_featured = models.IntegerField(max_length=1)
class Meta:
db_table = 'countries'
And in views.py I write
def index(request):
countries = Country.objects.all().order_by('id')
context = {
"countries" : countries
}
return render(request, 'countries/index.html', context)
If I run http://127.0.0.1:8000/countries/ then it will load country listing page in frontend.
Now, I want http://127.0.0.1:8000/admin/countries/ to see backend listing page with custom admin template.
Please help me if someone know
If I add path('admin/countries/', include(('countries.urls', 'countries'), namespace = 'countries')), in urls.py then http://127.0.0.1:8000/admin/countries/ is also take same template page and does not show admin template.
In the admin.py of your countries app, do this:
Although note that this is for django 2.x. You didn't specifiy your version, but django 1.x isn't much different from the following example. I just skimmed 1.11 quick, and the 2 attributes I checked in the options section were the same here. If you get a bug, change to the version you need, and fix the attribute.
from .models import Country
from django.contrib import admin
#admin.register(Country)
class CountryAdmin(admin.ModelAdmin):
list_display = ['iso_code', 'name', 'is_featured']
list_editable = ['name'] # Add more here if you want to edit them inline.
list_filter = ['iso_code'] # add more to be able to filter your model
list_per_page = 10 # paginates the amount that show up per page
search_fields = ['name', 'iso_code'] # field names searched

admin.py: "model = Thing" ,what does this code mean?if without it what gonna happen?

every one,,I am reading a Django practice book,,I saw a code "model = Thing" in admin.py,,,however, when I remove "model = Thing",,,the web program still can run,the admin site looks no difference??,what does this code mean?if without it what gonna happen? my models.py class is Thing
admin.py
from django.contrib import admin
from collection.models import Thing
class ThingAdmin(admin.ModelAdmin):
model = Thing #if I remove this code, the program still can run,,why need this code
list_display = ('name', 'description',)
prepopulated_fields = {'slug': ('name',)}
admin.site.register(Thing, ThingAdmin)
modles.py
from django.db import models
class Thing(models.Model):
name = models.CharField(max_length=255)
description = models.TextField()
slug = models.SlugField(unique=True)
Setting a model attribute on the ModelAdmin class will have no effect. You can safely remove that line from your code.
In the Django admin, you specify the model when you call admin.site.register(), or by using the register decorator. This allows you to use the same model admin class for more than one model.
admin.site.register(Thing, ThingAdmin)
admin.site.register(OtherThing, ThingAdmin)
As Jon pointed out in the comments, you do need to specify the model for InlineModelAdmin objects.