I'm building a RSS feed reader where users can add own feeds and categories. I want to store content all of the the feeds in db and run a script that would fetch that content for all of the feed_url present in db periodically (I had working project where each feed is fetched live and it wasn't good idea)
Now after several attempts I got into the following model structure.
class Category(models.Model):
name = models.CharField(unique=False, max_length=64)
user = models.ForeignKey(User)
def __unicode__(self):
return self.name
class Meta:
ordering = ('name',)
class Feed(models.Model):
feed_url = models.URLField(unique=True)
def __unicode__(self):
return self.feed_url
class UserFeed(models.Model):
feed = models.ForeignKey(Feed)
title = models.CharField(max_length=256)
category = models.ForeignKey(Category)
user = models.ForeignKey(User)
def __unicode__(self):
return self.title
class Post(models.Model):
feed = models.ForeignKey(Feed, related_name='feed_posts')
## starting from here, my cronjob / script populates the data (using feedparser lib)
title = models.CharField(max_length=256)
content = models.TextField()
link = models.URLField(max_length=512)
dt_published = models.DateTimeField()
dt_cached = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return '%s (%s)' % (self.feed, self.title)
class Meta:
ordering = ('-dt_published',)
What I'm looking for now is ability for pull into a view and later into templates, the following entities:
->Category -> Feed -> Posts
In my previous version where Feed and UserFeed fields were stored in one table/model, I had such view:
def category(request, category_id):
user = request.user
page_title = "Category: "
category = get_object_or_404(Category.objects.filter(id=category_id, user=user)).feed_set.all()
category_name = Category.objects.get(id=category_id)
context = {
'page_title': page_title,
'category': category,
'category_name': category_name,
}
return expand_context_and_render(request, context, 'reader/category.html')
and this is my template:
{% for feed in category %}
<p>{{ feed.title }}</p>
{% for post in feed.post_set.all|slice:"6" %}
<p>{{ post.title }}</p>
<p>{{ post.content }}</p>
<p>{{ post.dt_published|timesince }} ago.</p>
{% endfor %}
{% endfor %}
And it was working as intended.
Now, since I don't want to have multiple copies of feed_url in my database, I think the new models are OK, but I simply can't figure out how to query and display this new structure which has upstream and downstream relationships. I was looking at select_related but didn't get it to work. Could you please help ?
I eventually found the solution myself. It's bit hacky :( but does the job.
def category(request, user_category_id):
user = request.user
page_title = "Category: "
user_category = get_object_or_404(Category.objects.filter(id=user_category_id, user=user))
user_feeds = UserFeed.objects.filter(category=user_category, user=user)
context = {
'page_title': page_title,
'user_category': user_category,
'user_feeds': user_feeds,
}
return expand_context_and_render(request, context, 'reader/category.html')
and template:
{% for user_feed in user_feeds %}
<p><strong>{{ user_feed }}</strong></p>
{% for post in user_feed.feed.post_set.all|slice:"6" %}
<div class="col-md-4">
<p>{{ post.title|truncatechars:"45" }}</p>
<p>{{ post.content|striptags|safe|truncatechars:"85" }}</p>
<p class="feed_date">{{ post.dt_published|timesince }} ago.</p>
<hr />
</div>
{% endfor %}
{% endfor %}
Related
I need to initialized a ModelChoiceField value to an instance retrieved in get_context_data() but it does not seem to work. I have tried .initial= but no luck so far.
This is the form
class PostEditForm(forms.ModelForm):
post_category = forms.ModelChoiceField(label='Category',queryset=Category.objects.all(),empty_label="--Select Category--",required=False)
class Meta:
model=Post
fields=('title','summary','content')
and this is the view
class EditPostView(LoginRequiredMixin, AjaxableResponseMixin, UpdateView):
model = Post
form_class = PostEditForm
template_name = 'postapp/editpost.html'
success_url = reverse_lazy('postapp:draftposts')
def get_context_data(self):
context = super().get_context_data()
post=self.get_object()
print(post)
try:
post_category = PostCategory.objects.get(post=post)
print(post_category)
except:
print("Post Category not yet assigned")
else:
context['form']['post_category'].initial = post_category
print(context['form']['post_category'])
return context
and here is the post category model
class PostCategory(models.Model):
post = models.ForeignKey(Post,on_delete=models.CASCADE)
category = models.ForeignKey(Category,on_delete=models.CASCADE)
def __str__(self):
return self.category.title
UPDATE
I forgot to mention I was rendering the form manually, here is the HTML
<select class="form-select" name={{form.post_category.html_name}} id='{{form.post_category.auto_id}}'
{% if form.post_category.field.required %}required{% endif %}>
{% for value, text in form.post_category.field.choices %}
<option
{% if form.post_category.value == value|stringformat:"s" %}selected="selected"{% endif %} value={{value}}>{{text}}
</option>
{% endfor %}
</select>
The value in the initial needs to be PK.
So:
context['form']['post_category'].initial = post_category.id
Also, since post and post category are mapped 1-to-many, PostCategory.objects.get() can return multiple post categories which raises MultipleObjectsReturned error. I suggest using a combination of filter and first so you get the first category if there are many. You will probably change this logic to something that makes sense but it's important to be aware of it.
post_category = PostCategory.objects.filter(post=post).first()
Bonus points: override the get_initial method as it's better practice when using class based views.
def get_initial(self):
initial = super().get_initial()
post=self.object
print(post)
post_category = PostCategory.objects.filter(post=post).first()
if post_category is not None:
print(post_category)
initial.update({'post_category': post_category.id})
print(initial['post_category'])
else:
print("Post Category not yet assigned")
return initial
For a small listing of events I used an InlinePanel in my page model. Now I would like to filter these events by date, like I would do when using a #property with subpages: date__gte=date.today() for only displaying the future events on the page TourdatenIndexPag. How to achieve that?
My implementation:
class EventItem(LinkFields):
date = models.DateField("Datum")
...
panels = [FieldPanel('date')]
class Meta:
abstract = True
class TourdatenPageEventItem(Orderable, EventItem):
page = ParentalKey('md.TourdatenIndexPage', related_name='event_items')
class TourdatenIndexPage(Page):
...
content_panels = Page.content_panels + [
InlinePanel('event_items', label="Events"),
]
Where and how could these event_items be accessed and filtered?
Create a method on your page model to return the queryset you want:
class TourdatenIndexPage(Page):
def future_event_items(self):
return self.event_items.filter(date__gte=date.today())
Then, on your template, you can refer to self.future_event_items:
{% for event in self.future_event_items %}
<li>{{ event.date }}</li>
{% endfor %}
I am trying to implement a plugin for django-cms that shows a tree of of links. What I want to do is filter this tree based on what config the user chooses in my CMS. So based on the node he chooses in the config I want to be able to display only that sub-tree.
Here is my models.py
from django.db import models
from mptt.models import MPTTModel, TreeForeignKey
from cms.models.pluginmodel import CMSPlugin
class Section(MPTTModel):
name = models.CharField(max_length=25, unique=True)
parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True)
class MPTTMeta:
order_insertion_by = ['name']
def __str__(self):
return self.name
class SectionConfig(CMSPlugin):
root_shown = models.ForeignKey("Section")
title = models.CharField(default="Usefull Links", max_length=25)
Here is my cms_plugins.py:
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
from cms.models.pluginmodel import CMSPlugin
from django.utils.translation import ugettext_lazy as _
from links_plugin.models import Section, SectionConfig
class LinksPlugin(CMSPluginBase):
name = _("Links Tree Plugin")
model = SectionConfig
render_template = "links.html"
cache = False
def render(self, context, instance, placeholder):
context['instance'] = instance
context['nodes'] = Section.objects.all()
return context
plugin_pool.register_plugin(LinksPlugin)
and here is my templates/links.html
<div class='container-fluid'>
<h1>Liens Utiles</h1>
{% load mptt_tags %}
<ul class="root">
{% recursetree nodes %}
<li>
{{ node.name }}
{% if not node.is_leaf_node %}
<ul class="children">
{{ children }}
</ul>
{% endif %}
</li>
{% endrecursetree %}
</ul>
</div>
So my issue is giving the right set of nodes to my context. So in my cms_plugins.py id like to change
context['nodes'] = Section.objects.all()
to a filter that would build the subtree based on my
root_shown = models.ForeignKey("Section")
The problem is I don't know how to use the FK to reference my Section object and find my new root Section. From then I thought that I could use the get_descendants(include_self=True) to rebuild this new sub-tree and display that list of nodes. Am I wrong? How to I reference my desired node?
If possible ELI5
You'll want something like this:
contect['nodes'] = instance.root_shown.get_descendants(include_self=True)
I would like to return the field values of a SearchDocument's object. For example, I have generated a SearchResult object using:
class SearchResult(Handler):
def get(self):
index = search.Index("INDEX_NAME")
results = index.search("Brian")
self.render('search-result.html', results = results)
The results object looks something like this:
search.SearchResults(results=[
search.ScoredDocument(
doc_id=u'6122080743456768',
fields=[search.TextField(name=u'full_name', value=u"Brian Jones"),
language=u'en',
rank=106509239L),
search.ScoredDocument(
doc_id=u'4714705859903488',
fields=[search.TextField(name=u'full_name', value=u"Brian Lara"),
language=u'en',
rank=106427057L)],
number_found=2L)
Inside search-result.html, how can I return the values of the fields?
...
<body>
{{ field_values }} #return the field values "Brian Lara" and "Brian Jones"
<body>
...
Start here:
{% for result in results %}
{{ result.fields[0].value }}
{% endfor %}
(You have a '[' mismatch in your fields. Fix that, then check the syntax)
I have a Blog model which contains 4 entities, subject, blog, time_created and day_created. I query all the values and assign it to an object which pass to a jinja2 template. In the template I iterate over the object and print each different entity which results in the display of each blog post.
The model for the data is:
class Blog(db.Model):
subject = db.StringProperty(required = True, multiline = True)
blog = db.TextProperty(required = True)
time_created = db.DateTimeProperty(auto_now_add = True)
day_created = db.DateProperty(auto_now_add = True)
And I query all the entries to display as individual posts as such:
posts = db.GqlQuery('SELECT * FROM Blog ORDER BY time_created DESC')
and I pass these out to the template
class Mainpage(BaseHandler):
def get(self):
posts = Blog_posts()
logging.info(posts)
if posts:
end_time = time.time() - start_time
logging.info(end_time)
logging.info(start_time)
userLogin = self.checkLogin()
cookieVal = self.read_secure_cookie('user-id')
if cookieVal:
u_id = cookieVal.split('|')[0]
usr_instance = Users.get_by_id(int(u_id))
if usr_instance:
name = usr_instance.username
if userLogin == True and name:
self.render("blog.html", posts = posts, userLogin = userLogin, user_name = name, end_time = int(end_time))
logging.info("Logged in = " + str(userLogin))
else:
self.render("blog.html", posts = posts, userLogin = userLogin, user_name = "", end_time = int(end_time))
else:
self.render("blog.html", posts = posts, userLogin = userLogin, user_name = "", end_time = int(end_time))
the function render() is :
def render(self, template, **kw):
self.response.out.write(render_str(template, **kw))
I have a main template blogbase.html which I inherit to all other pages and it contains these lines which I use for template inheritance :
<div class="content">
{% block content %}
{% endblock %}
</div>
and in the html file that displays the main blog page and inherits this is :
{% extends "blogbase.html" %}
{% block content %}
{% for post in posts %}
<div class="one_post">
<div class="subject_title">
<div class="day">{{post.day_created.strftime("%d %b %Y")}}</div>
<a href="/blog/{{post.key().id()}}" target="_blank">
{{post.subject}}
</a>
</div>
<div class="post">
{{post.blog}}
</div>
</div>
{% endfor %}
<div class="query">
Queried {{end_time}} seconds ago
</div>
{% endblock %}
I added a debugging line to see whether the query works and it returns the true with the following :
<google.appengine.ext.db.GqlQuery object at 0x7f99f03cd050>
But I am not able to iterate over this object as in the templates, regular html works, but the part between the for loop doesn't render.
Is there something wrong in my query ? Or am I using the templating engine wrong ?
2 things I've noticed.
You are using a query in your templates to iterate over it and do what? Print info? You need to get the entities. Either get them when you query or iterate and fetch.
posts = db.GqlQuery('SELECT * FROM Blog ORDER BY time_created DESC').fetch(1000)
Also if you are passing dicts to jinja you might wanny check that you are using {% for post in posts.iteritems() %} but this is not the case here.