35👍
class RowInline(admin.TabularInline):
model = Table.rows.through
fields = ['name']
This presents a problem because Table.rows.through represents an intermediate model. If you would like to understand this better have a look at your database. You’ll see an intermediate table which references this model. It is probably named something like apname_table_rows. This intermeditate model does not contain the field, name. It just has two foreign key fields: table and row. (And it has an id field.)
If you need the name it can be referenced as a readonly field through the rows relation.
class RowInline(admin.TabularInline):
model = Table.rows.through
fields = ['row_name']
readonly_fields = ['row_name']
def row_name(self, instance):
return instance.row.name
row_name.short_description = 'row name'
class TableAdmin(admin.ModelAdmin):
inlines = [
RowInline,
]
exclude = ('rows',)
9👍
Django can not display it as you expected. Because there is an intermediary join table which joins your tables. In your example:
admin.py:
class RowInline(admin.TabularInline):
model = Table.rows.through # You are not addressing directly Row table but intermediary table
fields = ['name']
As the above note, model
in RowInline
addressing following table in your database, not Your Row
table and model
table: your-app-name_table_row
--------------------------------
id | int not null
table_id | int
row_id | int
You can think it like there is an imaginary table in your model that joins the two tables.
class Table_Row(Model):
table = ForeignKey(Table)
row = ForeignKey(Row)
So if you edit your Inline as following
class RowInline(admin.TabularInline):
model = Table.rows.through # You are not addressing directly Row table but intermediary table
fields = ['row', 'table']
you will not see any error or exception. Because your model
in RowInline
addresses an intermediary table and that table do have those fields. Django can virtualize the imaginary table Table_Row
up to here and can handle this.
But we can use relations in admin, with using __
. If your code do have a ForeignKey
relation instead of ManyToManyField
relation, then following be valid in your admin
class Row(models.Model):
name = models.CharField(max_length=255)
class Table(models.Model):
rows = models.ForeignKey(Row, blank=True)
name = models.CharField(max_length=255)
def __unicode__(self):
return self.name
and your admin:
class RowInline(admin.TabularInline):
model = Table
fields = ['rows__name']
Because you will have real Models and djnago can evaluate __
relation on them
But if you try that in your structure:
class Row(models.Model):
name = models.CharField(max_length=255)
class Table(models.Model):
rows = models.ManyToManyField(Row, blank=True)
name = models.CharField(max_length=255)
def __unicode__(self):
return self.name
and your admin:
class RowInline(admin.TabularInline):
model = Table.rows.through
fields = ['row__name']
it will raise Exception
! Because you do not have real table in your model, and django can not evaluate __
relations on virtual models it designs on top of its head.
Conclusion:
In your Inline
s addressing ManyToMany
relations, you are dealing with an imaginary intermediary model and you can not use fields
orexclude
attributes on that because your imaginary model do not have those fields and django can not handle relations ober that imaginary table. Following will be acceptable
class RowInline(admin.TabularInline):
model = Table.rows.through
# No fields or exclude declarations in here
and django will display combo boxes for your virtual intermediary table options and add a fancy green +
sign to add new records, but you will can not have inline fields to add new records directly to your database within the same single page. Djnago can not handle this on a single page.
You can try creating real intermediary table and show it using through, but thats totally a longer job and I do not test it to see its results.
Update: There is also the reason why django do not let something like that. Consider following:
Table | Table_Row | Row
-----------+-----------+---------
Start 5 | |
Step 1 5 | | 1
Step 2 5 | 5-1 | 1
At the beginning, You have a table with no related Rows, you want to add a row to the table… For joining a row with a table, you must first create a row so you execute step 1. After You do create your row, you can create Table_Row
record to join these two. So in contains more than a single database insertion. Django crew may avoid such usage since it contains multiple inserts and operation is related more tables.
But this is just an assumption on the reason of the behavior.
0👍
In your admin.py try this
class RowInline(admin.TabularInline):
model = Table.rows.through
list_display = ('name',)
class TableAdmin(admin.ModelAdmin):
inlines = [
RowInline,
]
readonly_fields = ('rows',)