Reputation: 1623
I got two Django models linked by a Foreign key:
class Author(models.Model):
...
class Book(models.Model):
author = models.ForeignKey(Author)
Please consider admin example below (I want to do the opposite):
from django.contrib import admin
from my_app.models import Author, Book
class BookInline(admin.TabularInline):
model = Book
class AuthorAdmin(admin.ModelAdmin):
inlines = [
BookInline,
]
admin.site.register(Author, AuthorAdmin)
With this example, we can see in the admin all authors, and for each author, their books.
Now, I would like to to it the other way around. Have en entry per Book in the administration and display the Author informations (it can be readonly) on the Book details. I tried with the solution below, but obviously it didn't work:
from django.contrib import admin
from my_app.models import Author, Book
class AuthorInline(admin.TabularInline):
model = Author
class BookAdmin(admin.ModelAdmin):
inlines = [
AuthorInline,
]
admin.site.register(Book, BookAdmin)
Django raised an error :
<class 'my_app.admin.AuthorInline'>: (admin.E202) 'my_app.Author' has no ForeignKey to 'my_app.Book'.
Do you know how to do that ?
More context :
Upvotes: 6
Views: 7037
Reputation: 5634
There's ModelAdmin.readonly_fields
that can take callables on both the ModelAdmin itself or the Model and show them on the add/change form. (docs)
If you wanted to show an author's name on the book change form in the admin, you would do it like this:
class BookAdmin(admin.ModelAdmin):
readonly_fields = ['get_author_name']
[...]
def get_author_name(self, book):
return book.author.name
All the readonly fields will be displayed beneath the form's regular fields.
You can also customize the way fields are displayed by modifying ModelForm.get_fields()
.
If you do it like this, you save yourself the trouble of overwriting the templates.
Upvotes: 4
Reputation: 1623
Here my solution, maybe not the best, but it works! :)
The idea is to change the template and therefor be able to display it the way I wanted.
admin.py :
class BookAdmin(admin.ModelAdmin):
...
change_form_template = 'admin/book_details.html'
def change_view(self, request, object_id, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['book'] = Book.objects.get(id=object_id)
extra_context['author'] = extra_context['book'].author
return super().change_view(request, object_id, form_url, extra_context=extra_context)
admin.site.register(Book, BookAdmin)
For the template I just copy/past the template change_form.html from the admin.
admin/book_details.html:
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static admin_modify %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %}
{% block coltype %}colM{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
› {% if has_change_permission %}<a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>{% else %}{{ opts.verbose_name_plural|capfirst }}{% endif %}
› {% if add %}{% blocktrans with name=opts.verbose_name %}Add {{ name }}{% endblocktrans %}{% else %}{{ original|truncatewords:"18" }}{% endif %}
</div>
{% endblock %}
{% block content %}
# Display here want you want !
{% endblock %}
I took a look at Django admin's template to be able to display my Book and Author the same way as if they were displayed by the default template. So my user won't be disrupted by this view.
If you found a better way, please let me know! :)
Upvotes: 7
Reputation: 599450
You can't do this. Inlines only make sense in one direction.
The way to do this is to define the display of the author field so that it gives you sufficient information automatically; for example, by defining the __str__
method.
Upvotes: 0
Reputation: 177
As the error says:
class 'my_app.admin.AuthorInline'>: (admin.E202) 'my_app.Author' has no ForeignKey to 'my_app.Book'.
You need to add the foreign key in Author table that references Book table.
The first one worked because you would have a foreign key in Book that references Author.
Upvotes: -1