How can I do to create/edit as when using PageAdmin and list as when not using PageAdmin?

88 views
Skip to first unread message

Rainell Dilou Gómez

unread,
Sep 14, 2017, 7:12:56 AM9/14/17
to Mezzanine Users
Hello,
I have created a custom content type and I have a problem with the listing in the admin panel. If I extends PageAdmin


admin.site.register(Ingredient, PageAdmin)

the create/edit page is rendered as in the image


and this is great for creating and editing, but the listing in the page section, as show in the following image, is a problem because the Ingredients (my custom content type) will be many, will be hundreds.


If instead I don't extend PageAdmin

admin.site.register(Ingredient)

my custom content type list is rendered as in the following image, in a more convenient way, considering that there will be hundreds of Ingredients

but the create/edit page will be rendered as in the next image, and it isn't convenient, considering that the fields are twice as many as are shown in this image.


So how can I do to create/edit as when using PageAdmin and list as when not using PageAdmin?

Joseph Mohan

unread,
Sep 14, 2017, 7:23:55 AM9/14/17
to mezzani...@googlegroups.com
Why not just have Ingredients as a normal Django model (not inheriting from Page). Similar to the Blog, or the Django Polls Tutorial? 

--
You received this message because you are subscribed to the Google Groups "Mezzanine Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mezzanine-users+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Rainell Dilou Gómez

unread,
Sep 14, 2017, 9:10:53 AM9/14/17
to Mezzanine Users
Yes, I've illustrated above, if I do it like in Django Polls Tutorial, just admin.site.register (Ingredient)

the listing will be ok, but in this case, the rendering of creating/editing page isn't convenient, considering the number of fields that need to be filled. For the creating/editing page would be much more convenient to obtain a result equal to that obtained using PageAdmin.

The most convenient result would be a simple listing (see third image), which we can obtain following the example of Django Polls Tutorial ( just admin.site.register(Ingredient) ) and an creat/edit page as the one we can obtain using PageAdmin ( dmin.site.register(Ingredient, PageAdmin). See first image ). But how to do it?

Joseph Mohan

unread,
Sep 14, 2017, 11:37:27 AM9/14/17
to mezzani...@googlegroups.com
Yes, i've ran into this sort of thing a few times and not been able to figure an easy way so I try to use non-Page models..

A solution that I have used is to create a custom admin page and show a list of your custom pages. Not an elegant solution at all.

I'm really interested to see if ayone else ever deals with this!

--

Eduardo Rivas

unread,
Sep 14, 2017, 11:46:18 AM9/14/17
to mezzani...@googlegroups.com

When I develop a Mezzanine site, there's usually two types of models I use:

1. Custom Page types. These are models that users want to incorporate into the page menu, and they must be editable/orderable from the page tree in the admin. In this case the model must inherit from mezzanine.pages.models.Page and the admin class must inherit from mezzanine.pages.admin.PageAdmin.

2. Displayable models. These are models we don't want to incorporate into the page menu, we want them to have their own section in the public site and their own list-enabled admin interface. The prime example is Mezzanine's built-in blog. It has it's own admin and it's own urls to display the BlogPost list and detail views. In this case the model must inherit from mezzanine.core.models.Displayable and the admin class from mezzanine.core.admin.DisplayableAdmin.

It looks to me like you want to go with approach #2. I suggest you re-write your models to use the Displayable base model and admin.

I gave a talk last year regarding this topic at DjangoCon. It explains how to convert a normal Django app into a Displayable-based Mezzanine app. Hopefully it'll be useful: https://www.youtube.com/watch?v=yVAmMXES2EU

To unsubscribe from this group and stop receiving emails from it, send an email to mezzanine-use...@googlegroups.com.

Rainell Dilou Gómez

unread,
Sep 14, 2017, 3:32:16 PM9/14/17
to Mezzanine Users
Eduardo I have seen the video of your explanation and it is exactly the solution for me, thank you very much.
In my opinion, for the beginners like me, would be of more help if the Mezzanine documentation were more explicit, with more examples.
Now that I have lived through this experience I can understand better the explanation in the Content Architecture section, where Displayable and Page are explained, but there is an example only for Page. That is why I believe that the documentation could be more specific and with more examples.
Thanks again.


Il giorno giovedì 14 settembre 2017 13:12:56 UTC+2, Rainell Dilou Gómez ha scritto:

Andrey inte

unread,
Sep 14, 2017, 6:14:44 PM9/14/17
to Mezzanine Users
Is there a way to migrate from Page to Displayable?
I have tried and it seems to be a pain...

Eduardo Rivas

unread,
Sep 14, 2017, 6:19:40 PM9/14/17
to mezzani...@googlegroups.com
What have you tried? Django should create the migration when you swap
out the subclasses. It's mostly going to remove fields since Page is a
subclass of Displayable anyways.

There's also the nuclear option of just migrating your app back to zero
to delete the tables and just create a new migration history with the
correct parent class. Most of the time I don't mind doing this in
development because it's just dummy data anyways. However, if losing
data is not an option, you can dump the Page-based data to a fixture,
manually remove the fields you don't need, and reuse the fixture to
restore the Displayable-based model.

Roger van Schie

unread,
Sep 14, 2017, 6:19:59 PM9/14/17
to mezzani...@googlegroups.com
Hi Rainell

I feel your pain with the lack of examples in the docs. I've had the exact same feeling when I first started. They seem to be aimed at those who know what they are doing, and as you learn more, they become more clear. Mezzanine is a great project, but it does have its quirks. 

Anyways, I've put together a small project to showcase the different types of inline editing that I think that might help you.

Catalog is the main page that will be shown. Create this first.
Recipe is of type displayable, and can be assigned to a catalog page.
Then, there are 3 different types of ingredient models, purely for demonstration purposes.

Ingredient1 in my opinion would be ideal for your situation, as you could have a library of ingredients, and then you just select all the ingredients from the list that are in your recipe, and you can reuse those ingredients in other recipes. The only draw back ( as far as my knowledge is concerned ) with this approach is that you cannot order the ingredients in the admin by dragging and dropping (and as someone who likes cooking, ALWAYS order your ingredient list in the order that they are required for your recipe ). I'm guessing the way to get around this ordering is to make it a manytomany but through another model that contains the ordering for that specific recipe. The hard part would be to get the admin to play along with this. Never tried it, but sounds like a good approach. There's an order_by templatetag that should be usefull for this. Haven't investigated it too much, but looks like the way to go. The blog app actually does the many-to-may admin interface  better by having two panes, once again, I haven't looked at it, but you can scratch in it's source code if you want to see how they have done that. If you're not sure where to find the file that governs how the blog app does the many to many with the two panes, let me know and I'll try and help you find it.

Ingredient3 & Ingredient4 are there to showcase the two orderable inline model admins that I know. The problem with this approach though is that you will have to create a separate banana for every recipe that requires banana, which could result in you eventually having 100 bananas which are exactly the same just because they are used in 100 recipes.

Hope this helps

(If anyone has any advice on the Ingredient1 ordering problem, I would love to hear it)

Roger


--

Rainell Dilou Gómez

unread,
Sep 15, 2017, 4:19:14 AM9/15/17
to Mezzanine Users
Hi, Roger.
I am very grateful for your kindness, also for understanding what it feels like to be a beginner. The example project you have published will be very useful for me, I greatly appreciate it.

I have seen how the many to many relationship is established in the admin.py file of the Blog application, they use filter_horizontal

filter_horizontal = ("categories", "related_recipes")

then it would be sufficient to add ingredients in the recipe class

filter_horizontal = ("categories", "ingredients", "related_recipes",)

Again, thank you very much for everything.



Il giorno giovedì 14 settembre 2017 13:12:56 UTC+2, Rainell Dilou Gómez ha scritto:

Joseph Mohan

unread,
Sep 15, 2017, 10:59:40 AM9/15/17
to mezzani...@googlegroups.com
Awesome!

That's a great talk Edwardo and a brilliant resource Roger.

I've been using Mezzanine for 2/3 years and only been coding for a few years longer than that, so getting my head round Mezzanine has been an excellent learning curve with code (and git).

Why don't you guys have this info/links up in the docs? I've only seen a few Mezzanine youtubes and examples are pretty thin. Or is there a good mezzanine resource list somewhere that i've missed?

I'm still a bit scared to contribute code-wise but if you want me to start putting together a resources list i'd love to help






--

Rainell Dilou Gómez

unread,
Sep 18, 2017, 7:39:26 AM9/18/17
to Mezzanine Users
Hello everyone,
I have done some tests and, in the end, I have obtained the best result by replicating the code of BlogPostAdmin, adapting it to my Ingredient model

ingredient_fieldsets = deepcopy(DisplayableAdmin.fieldsets)

ingredient_fieldsets
[0][1]["fields"].insert(1, "name")
ingredient_fieldsets
[0][1]["fields"].insert(1, "other_names")
ingredient_fieldsets
[0][1]["fields"].insert(1, "information")
ingredient_fieldsets
[0][1]["fields"].insert(1, "how_to_choose")
ingredient_fieldsets
[0][1]["fields"].insert(1, "nutrients")
ingredient_fieldsets
[0][1]["fields"].insert(1, "benefits")

ingredient_list_display
= ["title", "status", "admin_link"]

ingredient_fieldsets
= list(ingredient_fieldsets)
ingredient_list_filter
= deepcopy(DisplayableAdmin.list_filter) + ("name", "other_names", "information", "how_to_choose", "nutrients", "benefits")

class IngredientAdmin(DisplayableAdmin, OwnableAdmin):
   
"""
    Admin class for ingredients.
    """


    fieldsets
= ingredient_fieldsets
    list_display
= ingredient_list_display
    list_filter
= ingredient_list_filter
    filter_horizontal
= ("nutrients",)

   
def save_form(self, request, form, change):
       
"""
        Super class ordering is important here - user must get saved first.
        """

       
OwnableAdmin.save_form(self, request, form, change)
       
return DisplayableAdmin.save_form(self, request, form, change)

admin
.site.register(Ingredient, IngredientAdmin)

I have a single problem, if you can define problem, and is the order of the fields in the create/edit page, those aren't rendered in the order I have written in the code.



Il giorno giovedì 14 settembre 2017 13:12:56 UTC+2, Rainell Dilou Gómez ha scritto:

Roger van Schie

unread,
Sep 18, 2017, 8:17:13 AM9/18/17
to mezzani...@googlegroups.com
Hi Rainell

I've done the following in the past, which allows me to dictate the order they appear in.

form_fieldsets = deepcopy(PageAdmin.fieldsets)
form_fieldsets[0][1]["fields"][12:0] = ["hero_image", "hero_heading", "hero_text", "content_heading", "content", "button_text", "contact_email_sa", "contact_tel_sa", "contact_email_kenya", "contact_tel_kenya", "response" ]
form_fieldsets = list(form_fieldsets)

Regards
Roger

--

Rainell Dilou Gómez

unread,
Sep 18, 2017, 11:49:10 AM9/18/17
to Mezzanine Users
Thank you again Roger, now the fields are rendered in the correct order.

Someone knows why the language switchers for the inherited fields title and description are rendered, and aren't rendered for the keywords field (keywords_string)? However migration create the languages fields for keywords in the database: keywords_string_es, keywords_string_it, etc.

This is an example of the file translation.py

class IngredientTranslationOptions(TranslatedDisplayable, TranslatedRichText):
    fields
= ('title', 'description', 'keywords_string', 'information',)
translator
.register(Ingredient, IngredientTranslationOptions)

and this is an image of the result, language switchers for the keywords field aren't rendered. Someone knows why?






Il giorno giovedì 14 settembre 2017 13:12:56 UTC+2, Rainell Dilou Gómez ha scritto:
Reply all
Reply to author
Forward
0 new messages