Add menu items in wagtail ModelAdminGroup - django

I want to add non ModelAdmin items in a ModelAdminGroup as for example a MenuItem, like this:
MenuItem('A title', '/some_url/', icon_name='doc-full-inverse', order=1)
But I could not found any hints neither in Wagtail documentation nor on stackoverflow.
My ModelAdminGroup looks like this
class MyModelAdminGroup(ModelAdminGroup):
menu_label = "Some stuff"
menu_icon = "fa-suitcase"
menu_order = 1
items = (Model1Admin, Model2Admin)
I try to do this:
class MyModelAdminGroup(ModelAdminGroup):
menu_label = "Some stuff"
menu_icon = "fa-suitcase"
menu_order = 1
items = (Model1Admin, Model2Admin, MenuItem('A title', '/some_url/', icon_name='doc-full-inverse', order=1))
And some other idiotic stuff
But all I try crashed ...
I finally found an easy solution. I just write it bellow just in case it could help the community other people

I finally came with a trivial solution: just extending my custom ModelAdminGroup class and writing a specific get_submenu_items method:
class MyModelAdminGroup(ModelAdminGroup):
menu_label = "Some stuff"
menu_icon = "fa-suitcase"
menu_order = 1
items = (Model1Admin, Model2Admin)
def get_submenu_items(self):
menu_items = super().get_submenu_items()
menu_items.append(MenuItem('A title', '/some_url/', icon_name='doc-full-inverse', order=1))
return menu_items

Related

How do I override the results from an django API query

I'm very new to Django, so this might seem basic.
I have 2 models:
class Brand(models.Model):
brand_name = models.CharField()
class Foo(models.Model):
brand = models.ForeignKey(Brand, null=True)
foo_name = models.CharField()
Foo.brand can be null. Now when an API query is made on Foo, I want to include results where the brand's name matches but also where Foo.brand is null
If Foo looks like this:
Foo.brand, Foo.foo_name
1, "apple"
3, "orange"
1, "mango"
null, "grape"
/api/foo?brand=1 should return apple, mango and grape. How can I get this? I tried reading similar questions, it sounds like I need to override the viewset, but wasn't sure.
Thanks.
You can do it with override get_queryset method in your view class and use Q operator for creating or condition. You need to brand id is equal or null condition :
def get_queryset(self):
foos = Foo.objects.all()
brand_id = self.request.query_params.get('brand')
if brand_id:
foos = Foo.objects.filter(Q(brand_id__isnull=True)|Q(brand_id=brand_id))
return foos

django efficient queryset on related fields

Suppose you have the following models
class User:
pass
class Tag:
pass
class UserTag: # user is interested in tag
user = models.ForeignKey(User)
tag = models.ForeignKey(Tag)
class Blog:
tags = models.ManyToManyField(Tag)
For a given user, I want to get all blogs that user expressed his interests by UserTag
something like
Blog.objects.filter(tags__usertag_set__user=user_id)
I can do this in multiple steps but is it the best way to do it?
user_tags = UserTag.objects.filter(user=user)
result = Blog.objects.none()
for user_tag in user_tags:
tag = user_tag.tag
q = tag.blog_set.all()
result = result | q
This inevitably iterates all user_tags and bad...
you can do
user_tags = list(UserTag.objects.filter(creator=user).values_list('id', flat=True))
Blog.objects.filter(tags__in=user_tag)

django admin - custom fields radio - admin.HORIZONTAL not working

I have this custom field image_choice in django admin as a radio select.
IMG_CHOICES = (
('embed', _('Embed code')),
('file', _('Upload image')),
('link', _('Image Link'))
)
class BlogArticleForm(forms.ModelForm):
class Media:
js = ('js/myjs.js')
image_choice = forms.ChoiceField(choices=IMG_CHOICES, widget=forms.RadioSelect)
class BlogArticleAdmin(admin.ModelAdmin):
form = BlogArticleForm
fields = ['title', 'description', 'image_choice', 'image_embed', 'image_file', 'image_link']
admin.site.register(models.BlogArticle, BlogArticleAdmin)
I cannot get these radio buttons line up horizontally.
I tried:
radio_fields = {'image_choice': admin.HORIZONTAL}
and
radio_fields = {form.image_choice: admin.HORIZONTAL}
but I keep getting this error:
type object "BlogArticleForm" has no attribute 'image_choice'
how can I achieve this?
this is how it looks like right now:
Take a look at this line.
So I guess it should look something like this:
from django.contrib.admin.options import get_ul_class
class BlogArticleForm(forms.ModelForm):
image_choice = forms.ChoiceField(
choices=IMG_CHOICES,
widget=widgets.AdminRadioSelect(
attrs={'class': get_ul_class(admin.HORIZONTAL)}
))
UPD: I'm stupid :(
It says: if 'widget' not in kwargs: bla-bla-bla adds widget. So this should work with no widget:
image_choice = forms.ChoiceField(choices=IMG_CHOICES)
# And set
radio_fields = {'image_choice': admin.HORIZONTAL}

Django CMS Plugin using Inline InclusionTag results in Orphaned Plugin

I don't know what I am doing wrong as I have followed the documentation. I'm thinking it is some little thing. Here is the scenario:
The plugin is a Text Slideshow plugin. It allows the admin the ability to add text that will cycle like a slideshow.
The models are as follows:
class TextSlideshow(CMSPlugin):
label = models.CharField(max_length=128)
interval = models.IntegerField(
default=5000,
help_text=_('milliseconds between slides. (1000 equals 1 second)'))
def copy_relations(self, oldinstance):
for slide in oldinstance.text_slides.all():
slide.pk = None
slide.id = None
slide.text_slide_show = self
slide.save()
def __unicode__(self):
return self.label
class TextSlide(CMSPlugin):
text_slide_show = models.ForeignKey(TextSlideshow, related_name="text_slides")
display_value = models.CharField(max_length=128)
index = models.IntegerField(verbose_name=_("Display order"))
The inline is:
class TextSlideInline(admin.StackedInline):
model = TextSlide
fk_name = 'text_slide_show'
The plugin is:
class TextSlideshowPlugin(CMSPluginBase):
model = TextSlideshow
name = _("Text Slideshow")
render_template = "text_slideshow.html"
inlines = [TextSlideInline,]
module = _("Slideshow")
def __init__(self, model=None, admin_site=None):
super(TextSlideshowPlugin, self).__init__(model=model,
admin_site=admin_site)
for inline in self.inlines:
inline.placeholder = self.placeholder
inline.page = self.page
def render(self, context, instance, placeholder):
slides = instance.text_slides.all().order_by('index')
context.update({
'model': instance,
'placeholder': placeholder,
'slides': slides
})
return context
plugin_pool.register_plugin(TextSlideshowPlugin)
The plugin works and will run flawless, but when the admin user adds text slides like so:
When I run ./manage.py cms list plugins
I get this result:
==== Plugin report ====
There are 2 plugin types in your database
ERROR : not installed
instance(s): 2
TextSlideshowPlugin
model : cmsslideshow.models.TextSlideshow
instance(s): 1
As long as i don't run ./manage.py cms delete_orphaned_plugins my slideshow will stay in tact and will work fine.
The text slideshow itself is fine, it's just the inline'd elements which are orphaned.
Please help.
After reviewing my code with a microscope several times while re-reading the documentation and many examples, I found my issue.
The issue is the child model should inherit from models.Model, not CMSPlugin
change:
class TextSlide(CMSPlugin):
to:
class TextSlide(models.Model):

Django - How to customize a checkbox in a form?

I'm out of clues on how to customize a form with checkboxes. Basically I need to choose in the queryset only two choices of a list, I've tried to use "filter(pk=2, pk=3)" but it is not working. I've also to make the "pk=2" choice checked and not editable and the "pk=3" editable and not checked by default.
My form(does not work, filter(pk=2, pk=3) is not valid):
class PrimaryCategoryForm(forms.ModelForm):
primarycategorytype = forms.ModelMultipleChoiceField(queryset=PrimaryAdCategoryType.objects.filter(pk=2, pk=3), required=True, widget=forms.CheckboxSelectMultiple)
Any clues on how to achieve this?
Best Regards,
Trying building your queryset using the pythons union operator -
class PrimaryCategoryForm(forms.ModelForm):
set_1 = PrimaryAdCategoryType.objects.filter(pk=2)
set_2 = PrimaryAdCategoryType.objects.filter(pk=3)
qs = set_1 | set_2
primarycategorytype = forms.ModelMultipleChoiceField(queryset=qs,
required=True,
widget=forms.CheckboxSelectMultiple)
The | operator is creating a new set with all items from set_1 and all items from set_2.
UPDATE
Whilst the method above will support creating complicated sets. Martin's suggestion in the comments is probably sufficient if you simply want to pick a few pk's -
class PrimaryCategoryForm(forms.ModelForm):
qs=PrimaryAdCategoryType.objects.filter(pk__in=[2,3])
primarycategorytype = forms.ModelMultipleChoiceField(queryset=qs,
required=True,
widget=forms.CheckboxSelectMultiple)
EDITABLE CHECKBOXES
I think when it comes to making the checkboxes editable and preselected you may need to resort to javascript. Perhaps add data-attributes to the widget, then create a jquery function that manipulates the checkboxes on the basis of the data-attributes. Something like -
attrs = {'data-selected-value': 2, 'data-readonly-value': 2}
primarycateogrytype = forms.ModelMultipleChoiceField(attrs=attrs)
Then in the javascript -
var readonlyCheckboxValue = $('#your_mulliplecheckboxselect).data('selected-value');
var $readonlyCheckbox = $('#your_multiplecheckboxselect .input[value=readonlyCheckboxValue]');
$readonlyCheckbox.attr('readonly', true);
I haven't tested this code, but it should give you an idea of what I'm suggesting. Do check out the data-attribute if you haven't come across it already - it's pretty awesome.
Its possible that you would need the form to be dynamic in nature. You would need to override the __init__ method for the class.
Something like this here: How to Modify Choices of ModelMultipleChoiceField
An example:
class ServiceStartForm(forms.Form):
serviceList = service.models.Service.objects.all()
print serviceList.count()
b = {}
for aService in serviceList:
b[aService.id] = aService.name
c = b.items()
print "Within the form, ", serviceList.count()
serviceChoice = forms.ChoiceField(choices=c, widget=forms.Select())
input_directory = forms.CharField(max_length=200)
output_directory = forms.CharField(max_length=200)
def __init__(self, *args, **kwargs):
super(ServiceStartForm, self).__init__(*args, **kwargs)
serviceList = service.models.Service.objects.all()
b = {}
for aService in serviceList:
b[aService.id] = aService.name
c = b.items()
self.fields["serviceChoice"].choices = c