Django Filtering Admin Dropdowns - django

It seems this questions has been answered many times, but I am new to Django and apparently missing something when I try to work through the answers I have been finding. I have 2 Models, Model A is the main one and has a foreign key to Model B. When I make a new Model A in the Django Admin, I want the drop down for picking a Model B to only show the Model B's that have not already been assigned to a Model A. Can anyone point the right way?

You need to provide a custom form to the admin, telling it to only show a certain queryset for the Model B field:
from django import forms
from django.contrib import admin
from myapp.models import Person
class ModelAForm(forms.ModelForm):
model_b = ModelChoiceField(
queryset=ModelB.objects.exclude(model_a__isnull=False))
class Meta:
model = ModelA
class ModelAAdmin(admin.ModelAdmin):
form = ModelAForm

Related

Using database for forms django

I have an area where teachers can put homeworks for students.
Students can see these homeworks and when they click "send homework" button. they can fill a form so Teachers can see the homeworks from admin panel.
I have class to post homework.
I have 4 fields in class to post homework.
Fields = ["student number","Deadline","Lecture","Files"]
when students posting their homeworks, I want only use 2 ["student number","files"] fields for the form and I want other fields ["deadline","Lecture"] to be filled automatically from database.
What is the best way do it?
Quoting django model clean method docs:
Model.clean() This method should be used to provide custom model validation, and to modify attributes on your model if desired. For instance, you could use it to automatically provide a value for a field, or to do validation that requires access to more than a single field.
For your scenario:
import datetime
from django.db import models
class Homeworks(models.Model):
...
def clean(self):
self.Deadline = datetime.date.today() + some delta time
self.Lecture = ...
Also, remove this 'self-filled' fields from admin form interface:
class HomeworksAdmin(admin.ModelAdmin):
fields = ["student number","Files"]

Django: Search many to many field while creating object

I've got a use case where I have multiple Ingredient that can be linked to a Recipe through the Django admin. Now I have around a hundred ingredients which makes it very difficult to select the ingredients in the following UI.
Is there a way to add a search field or something similar to the django admin for easier selection?
You have few choices.
1. filter_horizontal
With filter_horizontal, you can use horizontal m2m ui in admin. I prefer this way using m2m in admin.
class YourAdmin(admin.ModelAdmin):
filter_horizontal = ('m2m_field',)
...
And the result will be...
2. raw_id_fields docs
You can use raw_id_fields for using pop-up modal with your m2m fields.
It's bit useful when you have lots of m2m field. Also, it's easy to filter which m2m obj to add.
class YourAdmin(admin.ModelAdmin):
raw_id_fiedls = ('m2m_field',)
...
I suppose you want to filter over ingredients and select it one by one on admin UI
You can use django forms builtin CheckboxSelectMultiple
widget in place of SelectMultiple to make selection easy
from django import forms
from django.contrib import admin
class RecipeForm(forms.ModelForm):
class Meta(object):
model = Recipe
widgets = {
'Ingredient': forms.CheckboxSelectMultiple,
}
class RecipeAdmin(admin.ModelAdmin):
form = RecipeForm
admin.site.register(Recipe, RecipeAdmin)
Alternatively, you can use django-better-filter-widget
package if you want a search input on choices, Refer Github repo for
installation
It is a custom widget, created by overriding SelectMultiple widget of
django forms
from django import forms
from django.contrib import admin
from better_filter_widget import BetterFilterWidget
class RecipeForm(forms.ModelForm):
class Meta(object):
model = Recipe
widgets = {
'Ingredient': BetterFilterWidget(),
}
class RecipeAdmin(admin.ModelAdmin):
form = RecipeForm
admin.site.register(Recipe, RecipeAdmin)

Left Join in Django Model Multi-table Inheritance in Admin app

Scenario:
Let's say I want to extend Django's User model using multi-table inheritance. Let's say the model I created for that is called CustomUser.
Now let's assume that there are already existing records in the database corresponding to the User model and the table corresponding to the CustomUser model is still empty.
Now I want CustomUser model to be accessible from the Django's Admin app. What I noticed is only User model records which has a corresponding record in the CustomUser table is included in the change list of CustomUser, as if an INNER JOIN is being done behind the scene in the query... (I checked using connection.queries and it was indeed an INNER JOIN).
Now I want to change this behaviour so that a LEFT JOIN is done to retrieve records pertaining to CustomerUser.
How can I achieve this in Django?
Thank you very much!
Put below in any working admin.py.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
#### below imports your custom user model
from accounts.models import CustomUser
admin.site.unregister(User)
class UserProfileInline(admin.StackedInline):
model = CustomUser
class UserProfileAdmin(UserAdmin):
inlines = [ UserProfileInline, ]
admin.site.register(User, UserProfileAdmin)
Above will show your CustomUser model in User in admin. now you can access User fields from UserProfileAdmin by User__first_name etc. pardon for bad english.

django - Joining LogEntry to actual models

so i'm using the admin LogEntry object/table to log events in my app. I have a view where i'd like to display each LogEntry.
It would be really great if i could join the LogEntry with the actual objects they represent (so i can display attributes of the object inline with the log entry)
In theory this should be easy as we have the model type and id from the LogEntry but i can't figure out how to join them using a queryset.
i thought i could just grab all the ids of the different objects and make another dictionary for each object type and then join them somehow (maybe zip the lists together?) but that seems dumb and not very djano-ish/pythonic.
does anybody have better suggestions?
** edit **
just want to clarify am not looking to use admin, but roll a custom view and template.
As I know Django uses contenttypes framework to perform logging in admin. So you should create generic relation inside your model and then to show inlines in admin use GenericTabularInline and GenericStackedInline. Please consult with the article.
from django.contrib import admin
from django.contrib.admin.models import LogEntry
from django.contrib.contenttypes.generic import GenericTabularInline
from django import forms
from some_app import models
from some_app.models import Item
class LogForm(forms.ModelForm):
class Meta:
model = LogEntry
class LogInline(GenericTabularInline):
ct_field = 'content_type'
ct_fk_field = 'object_id'
model = LogEntry
extra = 0
class ItemForm(forms.ModelForm):
class Meta:
model = Item
class ItemAdmin(admin.ModelAdmin):
form = ItemForm
inlines = [LogInline,]
admin.site.register(models.Item, ItemAdmin)
and you add to Item:
class Item(models.Model):
name = models.CharField(max_length=100)
logs = generic.GenericRelation(LogEntry)
this change won't create anything in your database, so there is no need to sync
Recent Django versions require to create a proxy for LogEntry:
from django.contrib import admin
from django.contrib.admin.models import LogEntry
from django.contrib.contenttypes.generic import GenericTabularInline
class LogEntryProxy(LogEntry):
content_object = GenericForeignKey('content_type', 'object_id')
class Meta:
proxy = True
class LogInline(GenericTabularInline):
model = LogEntry
extra = 0
class ItemAdmin(admin.ModelAdmin):
inlines = [LogInline,]
admin.site.register(models.Item, ItemAdmin)

Reorder users in django auth

I have a model that has a ForeignKey to the built-in user model in django.contrib.auth and I'm frustrated by the fact the select box in the admin always sorts by the user's primary key.
I'd much rather have it sort by username alphabetically, and while it's my instinct not to want to fiddle with the innards of Django, I can't seem to find a simpler way to reorder the users.
The most straightforward way I can think of would be to dip into my Django install and add
ordering = ('username',)
to the Meta class of the User model.
Is there some kind of monkeypatching that I could do or any other less invasive way to modify the ordering of the User model?
Alternatively, can anyone thing of anything that could break by making this change?
There is a way using ModelAdmin objects to specify your own form. By specifying your own form, you have complete control over the form's composition and validation.
Say that the model which has an FK to User is Foo.
Your myapp/models.py might look like this:
from django.db import models
from django.contrib.auth.models import User
class Foo(models.Model):
user = models.ForeignKey(User)
some_val = models.IntegerField()
You would then create a myapp/admin.py file containing something like this:
from django.contrib.auth.models import User
from django import forms
from django.contrib import admin
class FooAdminForm(forms.ModelForm):
user = forms.ModelChoiceField(queryset=User.objects.order_by('username'))
class Meta:
model = Foo
class FooAdmin(admin.ModelAdmin):
form = FooAdminForm
admin.site.register(Foo, FooAdmin)
Once you've done this, the <select> dropdown will order the user objects according to username. No need to worry about to other fields on Foo... you only need to specify the overrides in your FooAdminForm class. Unfortunately, you'll need to provide this custom form definition for every model having an FK to User that you wish to present in the admin site.
Jarret's answer above should actually read:
from django.contrib.auth.models import User
from django.contrib import admin
from django import forms
from yourapp.models import Foo
class FooAdminForm(forms.ModelForm):
class Meta:
model = Foo
def __init__(self, *args, **kwds):
super(FooAdminForm, self).__init__(*args, **kwds)
self.fields['user'].queryset = User.objects.order_by(...)
class FooAdmin(admin.ModelAdmin):
# other stuff here
form = FooAdminForm
admin.site.register(Foo, FooAdmin)
so the queryset gets re-evaluated each time you create the form, as opposed to once, when the module containing the form is imported.