可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Suppose I have car - suv/bus models.
I'd like to list all cars in the django admin (with name attribute).
When user clicks one of the car, it would go to the appropriate detail page of either suv or bus model.
How do I create a such admin list page?
class Car:
name = models.CharField()
class Meta:
abstract=True
class Suv(Car):
pass
class Bus(Car):
pass
回答1:
Not sure this is the best approach, but sharing my solution here.
First, create a Database View
create view [app_label_model_name] as
select id, name, 1 as car_type
from suv
union
select id, name, 2 as car_type
from bus
order by something;
then create non-manged model
class PostBaseView(models.Model):
# this would be CarBaseView, I'm copying my actual code
id = models.IntegerField()
raw_html = models.TextField(null=True)
created_at = models.DateTimeField(primary_key=True)
post_type = models.IntegerField()
class Meta:
managed = False
then, from admin page, change the links based on subclass types.
class ChangeList(ChangeListDefault):
def url_for_result(self, result):
# pk = getattr(result, self.pk_attname)
id = result.id
app_label = result.get_app_label()
model_name = result.get_model_name()
return reverse('admin:%s_%s_change' % (app_label,
model_name),
args=(quote(id),))
class PostBaseViewAdmin(admin.ModelAdmin):
list_display = ['__str__', 'post_type_str']
class Meta:
model = PostBaseView
def get_changelist(self, request, **kwargs):
"""
Returns the ChangeList class for use on the changelist page.
"""
return ChangeList
admin.site.register(PostBaseView, PostBaseViewAdmin)
volla you have a admin that shows multiple subclasses at one list.
回答2:
You need to make your Car
not an abstract model, as you have to have some base table for your vehicles.
class Car:
name = models.CharField()
class Meta:
abstract = False
class Suv(Car):
pass
class Bus(Car):
pass
class CarAdmin(admin.ModelAdmin):
model = Car
But here the easy things end. The admin doesn't have a built-in solution for your task. I suggest you overriding at least the CarAdmin.get_form()
and CarAdmin.get_queryset()
methods, though I'm not sure they are the only ones you have to customize.
回答3:
eugene's answer took me to the right direction. Thank you.
I'm not aware if there is a better way either, but in my case this approach solved the problem.
I already had a base class (Project
) with some generic fields (SportProject
and others), and specific classes that extends Project with their fields, and I wanted to have a single list of project, but wanted to have specific edit form for each project type.
In app/admin.py file, I did:
from django.contrib.admin.views.main import ChangeList as ChangeListDefault
from django.urls import reverse
class ProjectChangeList(ChangeListDefault):
def url_for_result(self, result):
app_label = result._meta.app_label.lower()
model_name = result.children._meta.object_name.lower()
return reverse('admin:%s_%s_change' % (app_label, model_name), args=(result.id,))
class ProjectDataAdmin(admin.ModelAdmin):
#...
def get_changelist(self, request, **kwargs):
"""
Returns the ChangeList class for use on the changelist page.
"""
return ProjectChangeList
In my model Project
, I have a children
property that returns the children instance (SportProject
for instance). Not sure if there is another way to have this structure in Django.
I also had all classes registered in django-admin (Project
and all children, as SportProject
), to have django-admin pages for those classes. Thus, I'm using django-modeladmin-reorder to hide undesired links on django-admin main page.
Hope it helps someone.
回答4:
I think this answer is applicable. ModelAdmin needs to know foreign key entries will be readonly, specified in a tuple called readonly_fields.
Using the problem that brought me here and there, I have (models.py):
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
def __str__(self):
return self.choice_text
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE, default = 1)
class Meta:
abstract = True
class Vote(Answer):
choice = models.ForeignKey(Choice, on_delete=models.CASCADE)
def answer(self):
return self.choice
def __str__(self):
return self.choice.choice_text
And (admin.py):
class VoteAdmin(admin.ModelAdmin):
#list_display = ('Answer.question.question_text', 'Answer.User.user_id', 'Choice.choice_text')
readony_fields = ('question', 'user')
list_display = ('question', 'user', 'choice')
fieldsets = [
('Question', {'fields': ['question']}),
('User', {'fields': ['user']}),
('Vote', {'fields' : ['choice']}),
]
Hope this proves useful to future searchers.