User manually create model fields for post [django] - python

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

Related

Django : submitting Foriegn key field with or without form object to models

we can submit foriegnkey data to models
through ModelForm
class BooksForm(forms.ModelForm):
class Meta:
model = Books
fields = "__all__"
where in templates we can add
{{form.author}} (author is foriegnkey field in books model)
Im aware that we can submit foriegnkey data using forms like this
but my question is.is there any way where we can submit a foriegnkey object which we have fetched using some other method (with or without form )
to a model(in my case its book)
Let me explain it in detail
lets say for instance there is a Search bar
when users search for author,
then the function fetches list of authors (choice field) to the user
where user can select and submit
which should get populated in the books model
there isnt any proper info related to this on web
all i could see is information on how to save data with Foriegnnkey using model form
any kind of insights is appreciated
I'm not 100% sure what your desired outcome is - this is how I understand your issue:
If you want to create a Book entry while passing an Author instance along you could set it as follows:
# models.py
class Author(models.Model):
name = models.CharField(max_length=50)
class Book(models.Model):
author = models.ForeignKey('Author', on_delete=models.CASCADE)
title = models.CharField(max_length=50)
# views.py
def example_view(request):
selected_author_instance = Author.objects.get(pk=1) # example query, adjust to your needs
# Create Book instance and pass author instance along
book_instance = Book.objects.create(
author=selected_author_instance,
title='example'
)
book_instance.save()
return render(...)

Tagulous - Displaying Posts based on filtering of self assigned tags

I have created a posts list where users can simply create a post, upload a photo and similar to StackOverflow, they can assign "Tags" to their post to categorise their post (I am using Tagulous library to power the tags feature of my application)
At the moment users can assign tags to their Post. However, what I am wanting to achieve is the following:
When a user creates a Post in a specific tag, I want to filter the overall Posts view, based on the past tags they have assigned to their previous posts. E.g. If User A assigns a tag called "Winter" to their Post, after submitting their post, they will see any other Posts on their Post Feed which are relevant to the "Winter" tag.
If in the future, they decide to assign one or more other tags to any future posts they create, those posts sharing the same tag name will also appear on their Post Feed.
In a nutshell, for every one tag which the user self assigns to any future post, they will begin to see Posts of other users who are using the same tag within their posts.
Where I am at, at the moment is:
I have created a public Post View (A feed of where all my posts are displayed arranged by date order)
I have created a view for where posts can be created and I have integrated Tagulous in to my Post Creation view.
Now, I am just trying to figure how to filter my posts based on which Tagulous tags the user has self assigned to their previous posts on my application.
Here is my code so far:
Models.py
class Post(models.Model):
content = models.TextField(max_length=1000)
date_posted = models.DateTimeField(default=timezone.now)
image = models.ImageField(default='default.png', upload_to='media')
author = models.ForeignKey(User, on_delete=models.CASCADE)
likes= models.IntegerField(default=0)
dislikes= models.IntegerField(default=0)
title = tagulous.models.SingleTagField(initial="test1, test2, test3")
season = tagulous.models.TagField(
autocomplete_view='tags_season_autocomplete'
)
Views.py
class PostListView(LoginRequiredMixin, ListView):
model = Post
template_name = 'core/home.html'
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = PAGINATION_COUNT
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
all_users = []
data_counter = Post.objects.values('author')\
.annotate(author_count=Count('author'))\
.order_by('-author_count')[:6]
for aux in data_counter:
all_users.append(User.objects.filter(pk=aux['author']).first())
data['preference'] = Preference.objects.all()
# print(Preference.objects.get(user= self.request.user))
data['all_users'] = all_users
print(all_users, file=sys.stderr)
return data
def get_queryset(self):
user = self.request.user
qs = Follow.objects.filter(user=user)
follows = [user]
for obj in qs:
follows.append(obj.follow_user)
return Post.objects.filter(author__in=follows).filter(season__name__in=['Winter']).order_by('-date_posted')
At the moment as seen in the last line of my Views.py file, I have managed to filter my Post Feed view based on manually defining the tag name (Although my goal here is to "get" all the tags from the "Posts" in which the user has created in the past and in return, filter the user's post feed based on the tags he has previously assigned to his or her previous posts on the application.
Any suggestions or pointers would be most appreciated!
(I have been referencing this source for ideas with forming a solution to my challenge - https://radiac.net/projects/django-tagulous/documentation/models/tagged_models/)
Thank you :-)
UPDATE:
So after a little more digging in to Tagulous, I have managed to change the last line of my views.py file to this:
return Post.objects.filter(author__in=follows).filter(season__author=self.request.user).order_by('-date_posted')
It's still not quite right as I am now receiving an error which says, "Cannot query must be an instance of Tagulous_Post_season"`
Could anyone suggest a method of what would be the most suitable way to create the instance?
Thanks!

Implementing one to many between an article and page models in Wagtail

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'.

Loading tree of relations at once

I've got model called Post:
class Post(models.Model):
poster = models.ForeignKey(User)
content = models.TextField(verbose_name='Text', max_length=1000)
reply_to = models.ForeignKey('self', null=True, blank=True, default=None)
This allows to add 'first post' (with blank reply_to), and reply to post and even 'reply to reply'
For example I've got in my database something like that:
First Post
Reply one
Reply to reply one
Reply two
Reply to reply two
How to load that tree of replies?
When I use:
r = Post.objects.filter(reply_to=FirstPost)
It returns of course:
Reply one
Reply two
Is it possible to load all related posts at once?
I need it mainly to count all replies to first post.
You can use MPTT (http://django-mptt.github.com/django-mptt/tutorial.html#the-problem). I have not used this library before so let me know how it goes.
models.py
class Post(MPTTModel):
poster = models.ForeignKey(User)
content = models.TextField(verbose_name='Text', max_length=1000)
parent = models.TreeForeignKey('self', null=True, blank=True, related_name='children')
class MPTTMeta:
order_insertion_by = ['poster']
views.py
....
r = FirstPost.get_children()
No, I don't think there is a way to load all replies at once.
But, you can add extra metadata to the post type to be able to run a in-order-style query, where counting the number of replies becomes a simple calculation with data already loaded for the parents node.
See this article on how you could do that (it uses the MySQL SQL dialect, and PHP, but the principles still apply).
Basically, you add left and right fields to the nodes in your tree that define an ordering, letting you easily count the number of items below a given root element in the tree. It's like a Binary Tree in a database table. The principle is taken from this excellent database design book: "Joe Celko's Trees and Hierarchies in SQL for Smarties".

dynamic slug with permanent redirect or store slug on database

Which is the best option for urls on a news page:
Dynamically generate slugs. Load the page from the object id. If the slug doesn't match, permanent redirect to the correct slug.
myweb.com/542/my-news-item
Cons i see: If the news title is changed the slug changes, but the old slug will redirect to the new one, so i dont know if it is a problem for lookups.
OR:
Static slug that will never change, even if the news title is changed.
myweb.com/my-news-item
Cons i see: One more field on the db. If i change the news title radically, the slug will be very different
If you want slugs to update, without breaking the old urls, you could separate slugs and link the latest in your list views, something along these lines should do it:
class Article(models.Model):
title = models.CharField() # etc
class ArticleSlug(models.Model):
article = models.ForeignKey(Article)
slug = models.SlugField(unique=True)
date_created = models.DateTime(auto_now_add=True, editable=False)
class Meta:
get_latest_by = "date_created"
In your enlisting template you could simply call the latest slug, but you'll probably want to have some M/Y/D hierarchy in the urls aswell
{{ article.title }}
If your site gets a lot of traffic, you might want to add a celery task that retrieves the latest slug and copies it to a field on your article model now and then. That will save you some SQL JOINs.
How about a combination of using the redirects app and a post save signal like what this guy wrote.

Categories