Skip to content

Commit

Permalink
More chapters
Browse files Browse the repository at this point in the history
  • Loading branch information
shabda committed Feb 22, 2018
1 parent c3ae468 commit eb04678
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 2 deletions.
2 changes: 1 addition & 1 deletion docs/edit_multiple_models.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ You have the :code:`Category` model, and you need to add and edit :code:`Villain

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
..
...

inlines = [VillainInline]

Expand Down
Binary file added docs/filter_fk_dropdown.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 15 additions & 1 deletion docs/filter_fk_dropdown.rst
Original file line number Diff line number Diff line change
@@ -1,2 +1,16 @@
How to filter FK dropdown values in django admin?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Your :code:`Hero` model has a FK to :code:`Category`.
So all category objects will show in the admin dropdown for category. If instead, you wanted to see only a subset,
Django allows you to customize that by overriding :code:`formfield_for_foreignkey`::

@admin.register(Hero)
class HeroAdmin(admin.ModelAdmin, ExportCsvMixin):
...
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "category":
kwargs["queryset"] = Category.objects.filter(name__in=['God', 'Demi God'])
return super().formfield_for_foreignkey(db_field, request, **kwargs)

.. image:: filter_fk_dropdown.png
24 changes: 24 additions & 0 deletions docs/many_fks.rst
Original file line number Diff line number Diff line change
@@ -1,2 +1,26 @@
How to manage a model with a FK with a large number of objects?
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

You can create a large number of categories like this::

categories = [Category(**{"name": "cat-{}".format(i)}) for i in range(100000)]
Category.objects.bulk_create(categories)


Now as :code:`Category` has more than 100000 objects, when you go to the :code:`Hero` admin, it will have category dropdown with 100000 selections.
This will make the page both slow and the dropdown hard to use.

You can change how admin hand;es it by setting the :code:`raw_id_fields`::

@admin.register(Hero)
class HeroAdmin(admin.ModelAdmin, ExportCsvMixin):
...
raw_id_fields = ["category"]

This change the Hero admin to look like:

.. image:: raw_id_fields_1.png

Add the popup looks like this

.. image:: raw_id_fields_2.png
Binary file added docs/one_to_one_inline.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/raw_id_fields_1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/raw_id_fields_2.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/single_admin_multiple_model.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions docs/single_admin_multiple_models.rst
Original file line number Diff line number Diff line change
@@ -1,2 +1,31 @@
How to create a single Django admin from two different models?
=================================================================


:code:`Hero` has a FK to :code:`Category`, so you can select a category from Hero admin.
If you want to also be able to create :code:`Category` objects from Hero admin, you can change the form for Hero admin, and customise the
:code:`save_model` behaviour.::

class HeroForm(forms.ModelForm):
category_name = forms.CharField()

class Meta:
model = Hero
exclude = ["category"]


@admin.register(Hero)
class HeroAdmin(admin.ModelAdmin, ExportCsvMixin):
form = HeroForm
....

def save_model(self, request, obj, form, change):
category_name = form.cleaned_data["category_name"]
category, _ = Category.objects.get_or_create(name=category_name)
obj.category = category
super().save_model(request, obj, form, change)


With this change, your admin looks like below and has allows creating or updating category from the Hero admin.

.. image:: single_admin_multiple_model.png
17 changes: 17 additions & 0 deletions heroes_and_monsters/entities/admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.contrib import admin
from django.db.models import Count
from django import forms

from .models import Hero, Villain, Category, Origin, HeroProxy, AllEntity, HeroAcquaintance

Expand Down Expand Up @@ -53,14 +54,24 @@ def export_as_csv(self, request, queryset):
class HeroAcquaintanceInline(admin.TabularInline):
model = HeroAcquaintance

class HeroForm(forms.ModelForm):
category_name = forms.CharField()

class Meta:
model = Hero
exclude = ["category"]


@admin.register(Hero)
class HeroAdmin(admin.ModelAdmin, ExportCsvMixin):
# form = HeroForm

list_display = ("name", "is_immortal", "category", "origin", "is_very_benevolent", "children_display")
list_filter = ("is_immortal", "category", "origin", IsVeryBenevolentFilter)
actions = ["mark_immortal"]
date_hierarchy = 'added_on'
inlines = [HeroAcquaintanceInline]
raw_id_fields = ["category"]

exclude = ['added_by',]

Expand Down Expand Up @@ -98,6 +109,9 @@ def children_display(self, obj):
# else:
# return []

def formfield_for_foreignkey(self, db_field, request, **kwargs):
return super().formfield_for_foreignkey(db_field, request, **kwargs)

def get_urls(self):
urls = super().get_urls()
my_urls = [
Expand All @@ -117,9 +131,12 @@ def set_mortal(self, request):
return HttpResponseRedirect("../")

def save_model(self, request, obj, form, change):
category_name = form.cleaned_data["category_name"]
if not obj.pk:
# Only set added_by during the first save.
obj.added_by = request.user
category, _ = Category.objects.get_or_create(name=category_name)
obj.category = category
super().save_model(request, obj, form, change)


Expand Down

0 comments on commit eb04678

Please sign in to comment.