Reputation: 221
I have a menu item that contains 4 resources, each language, if the user goes to EnResources i would like it to only display the Resources where the language field contains 'en' and the same with the other languages. So the issue is it is only ever getting the en items, no matter which menu item i choose its always the en items, not the FrResources or anything.
I am following the docs http://docs.wagtail.io/en/v2.5.1/reference/contrib/modeladmin/indexview.html#modeladmin-get-queryset
Models.py
class Resource(models.Model):
language = models.CharField(max_length=255, choices=constants.LANGUAGES)
title = models.CharField(blank=True, max_length=255)
resource_type = models.CharField(
choices=constants.RESOURCE_TYPES,
max_length=255
)
description = models.TextField()
link = StreamField(
blocks.BasicLinkBlock(max_num=1),
blank=True,
)
panels = [
FieldPanel('language'),
FieldPanel('title'),
FieldPanel('resource_type'),
FieldPanel('description'),
StreamFieldPanel('link'),
]
constants.py
RESOURCE_TYPES = (
('Documentation', 'Documentation'),
('Whitepaper', 'Whitepaper'),
('Webinar', 'Webinar'),
('Video', 'Video'),
('Testimonial', 'Testimonial'),
('ProductSheet', 'ProductSheet'),
)
LANGUAGES = (
('en', 'English'),
('fr', 'French'),
('be-fr', 'Belgique'),
('be-nl', 'Nederlands'),
)
WagtailHooks.py
class ResourceAdmin(ModelAdmin):
model = models.Resource
menu_label = 'Resources'
menu_icon = 'snippet' # change as required
list_display = (
'resource_type',
'title',
)
list_filter = (
'resource_type',
)
search_fields = (
'title',
'business_email',
)
class EnResourceAdmin(ResourceAdmin):
menu_label = 'English Resources'
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(language='en')
class FrResourceAdmin(ResourceAdmin):
menu_label = 'French Resources'
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(language='fr')
class BeResourceAdmin(ResourceAdmin):
menu_label = 'Belgium Resources'
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(language='be-fr')
class NlResourceAdmin(ResourceAdmin):
menu_label = 'Nederlands Resources'
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.filter(language='be-nl')
class ResourceAdminGroup(ModelAdminGroup):
menu_label = 'Resources'
menu_icon = 'snippet' # change as required
menu_order = 1000 # (000 being 1st, 100 2nd)
items = (
EnResourceAdmin,
FrResourceAdmin,
BeResourceAdmin,
NlResourceAdmin,
)
modeladmin_register(ResourceAdminGroup)
EDIT: I started doing a little more research and i found that according to the Django docs on default_manager. https://docs.djangoproject.com/en/2.2/topics/db/managers/#django.db.models.Model._default_manager
If you use custom Manager objects, take note that the first Manager Django encounters (in the order in which they’re defined in the model) has a special status. Django interprets the first Manager defined in a class as the “default” Manager, and several parts of Django (including dumpdata) will use that Manager exclusively for that model. As a result, it’s a good idea to be careful in your choice of default manager in order to avoid a situation where overriding get_queryset() results in an inability to retrieve objects you’d like to work with.
You can specify a custom default manager using Meta.default_manager_name.
If you’re writing some code that must handle an unknown model, for example, in a third-party app that implements a generic view, use this manager (or _base_manager) rather than assuming the model has an objects manager.
Note the last part of the first paragraph. I think that is exactly what is happening here.
Upvotes: 2
Views: 1141
Reputation: 5055
Your approach is correct. After you run your code, you will see 4 menu items with the names you have given, but as you said, all those pages will show the same data.
If you closely look at the URL
that your browser has gone to, you will see that no matter which menu item you click, browser redirects to the same URL
. You need to fix that.
To do that you have to make use of url_helper_class
variable. That accepts an instance of AdminURLHelper
class. You can find the source code of that class here.
If you read the source code, you will see that this class is generating url
s using the app_label
and model_name
. But as those two are same in this instance, no matter which menu item you click, this generates the same URL
.
You can override all these methods to perform a custom URL
generation as you like. But there is a simpler hack.
If you looked at the source code, you will see that the app_label
and the model_name
is taken from the model
's meta data. As those are the only two things that this class is getting from the meta data, you can simply override it with a custom class.
class MyOpts:
def __init__(self, model_name, app_label) -> None:
self.model_name = model_name
self.app_label = app_label
class MyUrlHelper(AdminURLHelper):
def __init__(self, model):
super().__init__(model)
self.opts = MyOpts(XXXX, model._meta.app_label)
Instead of passing the model_name
from meta data, you have to pass something else (stated as XXXX
in the code) to make URL
s unique for each menu item.
def get_helper(name):
class MyUrlHelper(AdminURLHelper):
def __init__(self, model):
super().__init__(model)
model_name = f"{model._meta.model_name}-{name}"
self.opts = MyOpts(model_name, model._meta.app_label)
return MyUrlHelper
class EnResourceAdmin(ResourceAdmin):
menu_label = 'English Resources'
url_helper_class = get_helper('en')
Now you will see that the menu items redirects to different pages and your filter have applied correctly. Hope this helps!!
Upvotes: 1
Reputation: 547
This is a great question and I had a similar issue recently.
You are correct that the first manager will be used as the default manager. A good way to get the outcome you are looking for is to define proxy models for each case of Resource
, and add custom managers for each of the proxy models. Then you can modify get_queryset
to return only instances where language = 'some language'
.
Upvotes: 0