So I want to create a post by letting the user add a section/subtitle as they need it, but I have no idea how to go about creating this, I currently just have a title, preview and content block in a form but want the user to be able to create as many subtitle and content blocks as they want. Any help is very much appreciated :)
Well that's really basic database design question... What you describe is a "one to many" relationship (one "post" has many "subtitle-plus-content"), which is implemented using a table for the "subtitle-plus-content" records, with a foreign key on the "post" table (so we know which "subtitle-plus-content" records belong to which "post" record).
In Django, this is simply done by creatin a matching model:
class Post(models.Model):
title = models.CharField(max_lenght=xxx)
content = models.TextField()
# etc
class ContentBlock(models.Model):
""" A block with additionnal subtitle and content for posts """
post = models.ForeignKey(Post, on_delete=models.CASCADE)
subtitle = models.CharField(max_lenght=xxx)
content = models.TextField()
# etc
You'll find more documentation here
I'm trying to setup a Wagtail site with an article to pages structure but I'm struggling. A review article for example may have an introduction page, a benchmark page and a conclusion page. I want to work out how to allow this relationship in wagtail and have it so that editors can add multiple pages to the same article on the same page. I can imagine the pages interface looking a bit like how you have content, promote and settings on pages but with the ability to add, rename and reorder pages. I've tried using a foreign key on a page model that links to an article but I can't get it to be shown in the admin the way I want.
Here is the django version of model layout I was looking to use. You have a parent article that is then made up of one or multiple pages. The pages should be editable, orderable and be created from within one panel in the admin with streamfields:
Class Article(models.Model)
STATE_DRAFT = 0
STATE_REVIEW= 1
STATE_PUBLICATION = 2
STATE_HIDDEN = 3
STATE = (
(STATE_DRAFT, 'draft'),
(STATE_REVIEW, 'pending review'),
(STATE_PUBLICATION, 'ready for publication'),
(STATE_HIDDEN, 'hide and ignore'),
)
title = models.CharField(_('title'), max_length=256)
slug = models.SlugField(
_('slug'), unique=True, blank=True, default='', max_length=256
)
description = models.TextField(
_('description'), max_length=256, blank=True, default=''
)
author = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='article'
)
publication = models.DateTimeField(
null=True, blank=True, default=None, db_index=True, help_text='''
What date and time should the article get published
'''
)
state = models.PositiveIntegerField(
default=0, choices=STATE, help_text='What stage is the article at?'
)
featured = models.BooleanField(
default=False,
help_text='Whether or not the article should get featured'
)
class Page(Page):
article = models.ForeignKey(
'Article', on_delete=models.CASCADE, related_name='pages'
)
title = models.CharField(max_length=256)
number = models.PositiveIntegerField(default=1) # So pages are ordered
body = models.TextField(blank=True)
As per my comment I don't think you'll be able to achieve everything you're looking for short of implementing an entirely bespoke CMS - but if you're able to bend the UI and data modelling requirements, then Wagtail's RoutablePageMixin is one possible way of achieving the general pattern of editing an article as a single unit, while presenting it as multiple pages on the front-end.
In this approach, you'd make Article a Wagtail Page model, with all of the sub-page content defined as fields (or InlinePanel child models) on that model. (If you want to split the content entry into tabs within the editing interface, see Customising the tabbed interface, although this won't support dynamically adding / reordering them.) You'd then define a URL route and template for each subpage of the article:
from wagtail.core.models import Page
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
class ArticlePage(RoutablePageMixin, Page):
intro = StreamField(...)
main_page = StreamField(...)
conclusion = StreamField(...)
#route(r'^$')
def intro_view(self, request):
render(request, 'article/intro.html', {
'page': self,
})
#route(r'^main/$')
def main_page_view(self, request):
render(request, 'article/main_page.html', {
'page': self,
})
#route(r'^conclusion/$')
def conclusion_view(self, request):
render(request, 'article/conclusion.html', {
'page': self,
})
In this example the three sub-pages are hard-coded, but with some more work (perhaps an InlinePanel child model with a slug field and a StreamField) you could make the subpages dynamic.
I saw gasman already provided an answer to you question, but I'm still going to write up an answer for two reasons:
I think you need some more pointers as to why gasmans' proposal is a better solution than yours, but it's way to much to write in a comment.
I have implemented a similar solution before, where there is a top level 'Article'-like object with multiple reorderable child objects, where the actual content resides.
Why you should make Article a Page subclass
You chose not to make Article a subclass of Page, and you said it was because the Article itself does not contain any content, only metadata about an article. That is not a very strange thought process, but I think you're looking at the wrong requirements for your Article model.
Let's look at Wagtail's own Page model. What kind of functionality does it provide out of the box?
It provides a tree structure with parent and child pages, so that your page can be placed somewhere in the hierarchy of your website
It provides a slug_field, so that Wagtail can automatically handle linking to your page.
It provides functionality for drafting, publishing and unpublishing.
Wagtail doesn't dictate anything about content, leaving you to decide what kind of content you want to put on your Page subclass, if any. Examples of Pages that do not have a body would be:
Contact forms.
Blog index pages.
Good questions you could ask when deciding whether you want a Model to be a subclass of a Page are:
Do I want this object to have it's own url?
Do I want to be able to place this object somewhere inside my website hierarchy?
Do I want to have SEO advantages for the object?
Do I want to be able to publish/unpublish this object or not?
In your case of the Article, you could say yes to almost al these question, so it'd be wise to make it a Page subclass. That way, you don't have to reinvent the wheel.
How you define the actual 'body' of your page is up to you.
You can place the actual content in either snippets, or subpages to that Article. Or you can even choose to create a list of StreamFields inside your model.
How to implement ordered subcontent.
I have implemented a structure like this before.
The way I did this was very similar to what gasman proposes.
In my case, I needed to create a website where you could find an object (like you article) and display different types of explanation modules for it. For each document, I created a ArticlePage, and for each explanation module, I created a snippet called ExplanationModule.
I then created a through model with an ordering, and added a RoutablePageMixin to the class like gasman explains.
The structure looks something like this:
#register_snippet
class ArticlePageModule(models.Model):
...
title = models.CharField(max_length=100)
body = StreamField(LAYOUT_STREAMBLOCKS, null=True, blank=True)
panels = [
FieldPanel('title'),
StreamFieldPanel('body'),
]
class ArticlePageModulePlacement(Orderable, models.Model):
page = ParentalKey('articles.ArticlePage', on_delete=models.CASCADE, related_name='article_module_placements')
article_module = models.ForeignKey(ArticlePageModule, on_delete=models.CASCADE, related_name='+')
slug = models.SlugField()
panels = [
FieldPanel('slug'),
SnippetChooserPanel('article_module'),
]
class ArticlePage(Page, RoutablePageMixin):
# Metadata and other member values
....
content_panels = [
...
InlinePanel('article_module_placements', label="Modules"),
]
#route(r'^module/(?P<slug>[\w\-]+)/$')
def page_with_module(self, request, slug=None):
self.article_module_slug = slug
return self.serve(request)
def get_context(self, request):
context = super().get_context(request)
if hasattr(self, 'article_module_slug'):
context['ArticlePageModule'] = self.article_module_placements.filter(slug = self.article_module).first().article_module
return context
What this does is the following:
Create a ArticlePageModule snippet, which is just some kind of content, like a title and a body.
Create a ArticlePageModulePlacement which links a ArticlePage to a module, and adds the following:
A slug
An Ordering (Because it subclasses the Orderable mixing)
Create a ArticlePage which does two things:
Define a ArticlePageModuleplacement panel, which allows you to add ArticlePageModulePlacements
Subclass RoutablePagemixin, as described in gasman's answer.
This provides you with a Wagtail-proof, reusable and robust way of creating Articles with SubContent.
The modules don't show up in tabs, but will be shown on the page's layout page under a panel called 'Modules'.
I'm working on a blog project. I want to set primary and secondary categories for the posts. For example,
primary category: Music, Movie, Computer
and secondary categories for each primary category like:
Music - Dance, Rock, Country /
Movie - Script, Teaser, Review /
Computer - Hardware, Software /
And when I create a new post I want to restrict secondary category choices according to the primary category I chose.
(to be precise, in the post creation form,
first, I want both primary and secondary categories to be shown in dropdown menus, and
second, after I choose a primary category I want only the secondary category choices which belong to the primary category I chose to be shown in the dropdown menu for secondary category.)
Currently my models.py:
class PrimaryCategory(models.Model):
title = models.CharField('Primary Category', max_length=50)
class SecondaryCategory(models.Model):
title = models.CharField('Secondary Category', max_length=50)
primary = models.ForeignKey(PrimaryCategory,on_delete=models.CASCADE)
class Post(models.Model):
title = models.CharField(max_length=256)
content = models.TextField()
create_date = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(User, null=True, on_delete=models.CASCADE)
primary_category = models.ForeignKey(PrimaryCategory, on_delete=models.CASCADE, null=True)
secondary_category = models.ForeignKey(SecondaryCategory, on_delete=models.CASCADE, null=True)
and I searched and I can maybe use ForeignKey.limit_choices_to in my ModelForms, but I'm stuck here. Could anybody kindly help writing my forms.py?
currently I only have forms.py like:
from django import forms
from myblog.models import Post,PrimaryCategory,SecondaryCategory
class PostForm(forms.ModelForm):
secondary_category = models.ForeignKey(
PrimaryCategory,
on_delete=models.CASCADE,
limit_choices_to= ??? ,
)
class Meta:
model=Post
to summerize:
how to show category choices in dropdown menu instead of free text input?
how to dynamically restrict secondary category choices according to primary category choice in the form?
(Added) 3. I have no working knowledge of javascript, so I want to do it without JS if possible..If it is not possible to change choices dynamically, then is it at least possible to make the form to give me an error message if secondary category choice I made is not under chosen primary cagetory? I think I'll work on it but any advice would be of great help.
I would appreciate any help. Thank you.
I googled and found exactly what I wanted here:
https://simpleisbetterthancomplex.com/tutorial/2018/01/29/how-to-implement-dependent-or-chained-dropdown-list-with-django.html
It needs jQuery and I actually didn't fully understand what's happening in the jQuery part of the solution, but otherwise everything's clear and it works perfect!
I have trouble to display the ''saved''/''liked'' posts of my users in django/admin. I would like to have a field in the Adminpage to show which user likes which posts. I made an Userprofile model where all extra information (besides the one on the given django admin user Profile) are stored. so here is my model View:
class UserProfile(models.Model):
user = models.OneToOneField(User, null=True)
#likes = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True,default=1, related_name='likes')
likedPosts=models.ManyToManyField('self')
Field1 = models.CharField(max_length=50,default='Sunny')
Field2 = models.CharField(max_length=50,default='')
class Meta:
ordering =['-user']
#def __unicode__(self):
# return self.user.username
User.profile =property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
right now in the liked post field I have only some usernames or "User object"
I tried all kinds of combinations to get the information into the admin page but as you can see I did not make it.
I tried to change the unicode and of course the liked post line. If you need more information please tell me so. I appreciate every kind of help.
django admin isn't really meant to support many to many relationships from both directions in the django admin. However, the link below contains a workaround that I think should address your problem with a better explanation of why many-to-many relationships are only shown from one side by default.
(many-to-many in list display django).
so for everybody who wants to do something similar this worked for me:
class UserProfile(models.Model):
likedPosts = models.ManyToManyField('self',default=None,blank=True)
def __unicode__(self):
return "{0}".format(self.user.likes.all())
I'm trying to replicate Groupon using Django. I'm basically trying to achieve the below
1. A Merchant can register with the site
2. Can start creating campaigns
For the merchant registration part , I'm importing the User model and including a random MerchantID generator using random.
class Merchants(models.Model):
merchant = models.OneToOneField(User)
MerchantID = models.CharField(max_length = 15)
Storename = models.CharField(max_length = 25)
def save(self):
self.MerchantID = MerchantIDgen()
super(Merchants,self).save()
def __str__(self):
return self.merchant.username
The merchant is able to successfully register with the site. However, I'm having trouble enabling the create campaigns part.
the html link to the create campaign part is:
{% if user.is_authenticated %}
<a href = '/Merchants/{{user.username}}/Campaign'> start a campaign </a><br/>
ideally , I would want the variable portion to pull in the MerchantID value from the user. But I cant seem to pull this value from the User model( which makes sense since its sitting in the Merchants table)
In order the accomplish the above, is it better for me to just scrap the User model and just do the registration from the Merchant Model alone? (am I right in assuming that by doing so , I wouldn't have access to user authentication etc?)
Or is there any other way to pull the MerchantID using the user model?
figured a workaround which basically is to filter using username from user and using that instance to filter the Merchants table