How To Avoid Logic In Template? - python

I'm trying to develop a forum application.
I'm trying to display the latest topic that's been posted in each category on a listing page. However, I realised after adding more than one category that I need a separate query for each single category or it just shows the newest topic overall.
I'm just not sure how to keep my logic in the view for the queries. Obviously, I could just perform the query inside of my for loop but that doesn't seem very MVT oriented.
Here's my views.py:
from django.shortcuts import render
from .models import ForumReply, ForumCategory, ForumTopic
def index(req):
categories = ForumCategory.objects.all()
#find latest topic or topic by reply
topic = ForumTopic.objects.latest('created_at')
reply = ForumReply.objects.latest('created_at')
if (topic.created_at > reply.created_at):
latest = topic
else:
latest = reply.topic
return render(req, "forum/category_listing.html",
{'categories': categories, 'latest': latest})
And my category_listing.html:
{% extends '__base.html' %}
{% block content %}
{% for category in categories %}
<div class="forum_category">
<h1>{{ category.title }}</h1>
{{ category.body }}
<br />
<em>Latest Post: </em> {{ latest.title }} by {{ latest.user }} at {{ latest.created_at|date:"D d F Y h:i" }}
</div>
<br />
{% endfor %}
{% endblock %}

You can create a custom template tag that returns the latest post for each category.
Something like this:
# views.py
def index(req):
categories = ForumCategory.objects.all()
return render(req, "forum/category_listing.html", {'categories': categories})
# templatetags/category_tags.py
#register.assignment_tag
def get_latest_post(category):
# perform logic here for selecting latest post for specific category
return latest
# category_listing.html
{% load category_tags %}
{% for category in categories %}
{% get_latest_post category as latest %}
<em>Latest Post: </em> {{ latest.title }} by {{ latest.user }} at {{ latest.created_at|date:"D d F Y h:i" }}
{% endfor %}
You can read the documentation for more information https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/#assignment-tags

Related

How to get count of friends of friends

i am building a website like instagram where users can follow friends, i have been able to implement follow friend and also displaying friends of friends (mutual friend). I was not able to get the count of friends of friends; this is what i tried:
Model:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True)
friends = models.ManyToManyField('Profile', related_name="my_friends",blank=True)
view:
#login_required
def profile_user_view(request, username):
#Friend of Friends
p = Profile.objects.filter(user__username=username).order_by('-id')
all_friends = request.user.profile.friends.values_list('pk', flat=True)
friends_of_friend = Profile.objects.filter(pk__in=all_friends)
context = {
'profile_img': p,
'friends_of_friend': friends_of_friend,
}
return render(...)
Template:
{% for data in profile_img %}
{% for friend in friends_of_friend %}
{% if friend in data.friends.all %}
<li>
<a href="{% url 'site:profile-view' friend.user.username %}" class="dark-grey-text">
<b>{{ friend.user.username|lower }}</b>
</a>
</li>
<li>
{{ friend.count }} #This is not showing the count of friends of friends, when i use length it displays '0 0' instead of '2' (mutual friends)
</li>
{% endif %}
{% endfor %}
{% endfor %}
You need to use friends_of_friend.count to get count of current user's friends. If you want every friend's friend count then use {{ friend.friends.count }}.
Honestly, you do not need this much context in template. You can access them all from {{ user }} attribute in template. For example:
{% for friend in user.friends.all %}
Username {{ friend.user.username | lower }}
Count {{ friend.friends.count }}
{% endfor %}
i know it is late but ....
utils.py:
from django.contrib.auth.models import User
from collections import Counter
#this function finds friends_of_friends_id
def friendship(user):
a = User.objects.get(id=user)
return [i.id for i in a.profile.friends.all()]
#this function will take return all your friends's id with how many mitual_friend you have together
def count_mitual_friends(user_id):
return Counter([foaf_id for friend_id in friendship(user_id)
for foaf_id in friendship(friend_id)
if foaf_id != user_id
and foaf_id not in friendship(user_id)
])

TypeError at /careers/test/ jobpost_detail() got an unexpected keyword argument 'slug'

I installed the app careers mezanine and after creating a test position and turn the page to see the error TypeError at / careers / test /
jobpost_detail () got an unexpected keyword argument 'slug'. How do I fix this problem?
views
from calendar import month_name
from django.shortcuts import get_object_or_404
from collections import defaultdict
from django.contrib.contenttypes.models import ContentType
from django import VERSION
from careers.models import JobPost
from mezzanine.conf import settings
from mezzanine.generic.models import AssignedKeyword, Keyword
from mezzanine.utils.views import render, paginate
def jobpost_list(request, tag=None, year=None, month=None, template="careers/jobpost_list.html"):
"""
Display a list of job posts that are filtered by year, month.
"""
settings.use_editable()
templates = []
jobposts = JobPost.objects.published()
if tag is not None:
tag = get_object_or_404(Keyword, slug=tag)
jobposts = jobposts.filter(keywords__in=tag.assignments.all())
if year is not None:
jobposts = jobposts.filter(publish_date__year=year)
if month is not None:
jobposts = jobposts.filter(publish_date__month=month)
month = month_name[int(month)]
# We want to iterate keywords and categories for each blog post
# without triggering "num posts x 2" queries.
#
# For Django 1.3 we create dicts mapping blog post IDs to lists of
# categories and keywords, and assign these to attributes on each
# blog post. The Blog model then uses accessor methods to retrieve
# these attributes when assigned, which will fall back to the real
# related managers for Django 1.4 and higher, which will already
# have their data retrieved via prefetch_related.
jobposts = jobposts.select_related("user")
if VERSION >= (1, 4):
jobposts = jobposts.prefetch_related("keywords__keyword")
else:
if jobposts:
ids = ",".join([str(p.id) for p in jobposts])
keywords = defaultdict(list)
jobpost_type = ContentType.objects.get(app_label="careers", model="jobpost")
assigned = AssignedKeyword.objects.filter(jobpost__in=jobposts, content_type=jobpost_type).select_related("keyword")
for a in assigned:
keywords[a.object_pk].append(a.keyword)
for i, post in enumerate(jobposts):
setattr(jobposts[i], "_keywords", keywords[post.id])
jobposts = paginate(jobposts, request.GET.get("page", 1),
settings.CAREERS_PER_PAGE,
settings.MAX_PAGING_LINKS)
context = {"jobposts": jobposts, "year": year, "month": month, "tag": tag}
templates.append(template)
return render(request, templates, context)
def jobpost_detail(request, template="careers/jobpost_detail.html"):
""". Custom templates are checked for using the name
``careers/jobpost_detail_XXX.html`` where ``XXX`` is the job
posts's slug.
"""
jobposts = JobPost.objects.published()
jobpost = get_object_or_404(jobposts)
context = {"jobpost": jobpost, "editable_obj": jobpost}
templates = [u"careers/jobpost_detail_%s.html" %(slug), template]
return render(request, templates, context)
html
{% extends "careers/jobpost_list.html" %}
{% load mezzanine_tags keyword_tags i18n %}
{% block meta_title %}{{ jobpost.meta_title }}{% endblock %}
{% block meta_keywords %}{% metablock %}
{% keywords_for jobpost as tags %}
{% for tag in tags %}{% if not forloop.first %}, {% endif %}{{ tag }}{% endfor %}
{% endmetablock %}{% endblock %}
{% block meta_description %}{% metablock %}
{{ jobpost.description }}
{% endmetablock %}{% endblock %}
{% block title %}
{% editable jobpost.title %}{{ jobpost.title }}{% endeditable %}
{% endblock %}
{% block breadcrumb_menu %}
{{ block.super }}
<li class="active">{{ jobpost.title }}</li>
{% endblock %}
{% block main %}
<h6>
{% trans "Posted" %} {{ jobpost.publish_date|timesince }} {% trans "ago" %}.
</h6>
{% editable jobpost.content %}
{{ jobpost.content|richtext_filter|safe }}
{% endeditable %}
{% keywords_for jobpost as tags %}
{% if tags %}
{% spaceless %}
<ul class="unstyled tags">
<li>{% trans "Tags" %}:</li>
{% for tag in tags %}
<li>{{ tag }}</li>
{% endfor %}
</ul>
{% endspaceless %}
{% endif %}
{% set_short_url_for jobpost %}
<a class="btn small primary share-twitter" target="_blank" href="http://twitter.com/home?status={{ jobpost.short_url|urlencode }}%20{{ jobpost.title|urlencode }}">{% trans "Share on Twitter" %}</a>
<a class="btn small primary share-facebook" target="_blank" href="http://facebook.com/sharer.php?u={{ request.build_absolute_uri }}&t={{ jobpost.title|urlencode }}">{% trans "Share on Facebook" %}</a>
{% endblock %}
url
from django.conf.urls import patterns, url
# Job Post patterns.
urlpatterns = patterns("careers.views",
url("^tag/(?P<tag>.*)/$",
"jobpost_list",
name="jobpost_list_tag"),
url("^archive/(?P<year>\d{4})/(?P<month>\d{1,2})/$",
"jobpost_list",
name="jobpost_list_month"),
url("^archive/(?P<year>.*)/$",
"jobpost_list",
name="jobpost_list_year"),
url("^(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>.*)/$",
"jobpost_detail",
name="jobpost_detail_date"),
url("^(?P<slug>.*)/$",
"jobpost_detail",
name="jobpost_detail"),
url("^$",
"jobpost_list",
name="jobpost_list"),
)
The error tells you exactly what is going on: your "jobpost_detail" URL the captures a slug parameter and passes it on to the view, but that view does not expect a slug, only the request and a template. Also, you are not doing anything in that view to get the actual post identified by a slug: you are always getting the first published post.
I suspect you want to do the following:
def jobpost_detail(request, slug, template="careers/jobpost_detail.html"):
jobposts = JobPost.objects.published()
jobpost = get_object_or_404(jobposts, slug=slug)

In django how to access 2 field values in custom django modeladmin...includes/fieldset.html?

Edited my code: In the custom fieldset of a model admin:
{%load app_extras %}
{% if field.field.name == 'mobile' %}
<a target="hiddenIframe" href="http://url_to_call.php?exten={{request.user.employee_profile.extension}}&phone={{ field.field.value }}">Click-to-call</a>
{% my_mobile mobile=field.field.value as mob %}
{% endif %}
{% if field.field.name == 'sms_message' %}{{ mob }}
<a target="hiddenIframe" href="http://url_for_send_sms.php?sms_message={{ field.field.value }}&phone={{ mob }}">Click-to-send-sms</a>
{% endif %}
Here I am trying to access mobile number as well as sms_message fields of the model admin form simultaneously.
I have figured that I need to use custom tags, so I created the templatetags module, with app_extras.py containiging the function to assign the value of mobile and return it as follows:
#register.assignment_tag
def my_mobile(*args, **kwargs):
m_mobile = int(kwargs['mobile'])
return {'m_mobile': m_mobile }
In the template fiedset.html above note changes: This returns a Long value as: {'m_mobile': 1234534519L}
When seen on the browser for url for hyperlink shows:
http://url_for_send_sms.php/?sms_message=fgdfg&phone={%27m_mobile%27:%1234534519L}
How do I access the mobile number? Is my custom tag correct?
I formatted the output in my tag as:
#register.assignment_tag
def my_mobile(*args, **kwargs):
m_mobile = ("%d" %int(kwargs['mobile']))
return {'m_mobile': m_mobile }
In the template fieldset.html changed the code as:
{% if field.field.name == 'sms_message' %}
<a target="hiddenIframe" href="http://url_for_send_sms.php?sms_message={{ field.field.value }}&phone=={{ mob.m_mobile }}">Click-to-send-sms</a>
{% endif %}
Important: Both the mobile number and the sms_message are in the same line of the fieldset in the django modeladmin (in my case). So above code belongs to the loop {% for line in fieldset %} loop
Try
{% for ln in fieldset %}
{% for fld in ln %}
{% if f.field.name == 'mobile' %}
{{ f.field.value }}
{% endif %}
{% endfor %}
{% endfor %}
Maybe this is not the best solution ... but it is solution :)

grouping fields in django form

I want to render form grouping fields. Form actually is created dynamically according to incoming dictionary
for f in settings.FORM_BIG_FIELDS:
self.fields[f['id']] = eval(f['type'])(label=f['label'], required=f.get('required', True))
self.fields[f['id']].groupp = f.get('group', 1)
groupp attribute means appropriate group, then I try to render it like
{% regroup form.fields.values by groupp as field_group %}
{% for group in field_group %}
<div class="group_{{ group.grouper }}">
{% for field in group.list %}
<p>
{{ field.all }}
{{ field }}
</p>
{% endfor %}
</div>
{% endfor %}
But as output I get the following
<django.forms.fields.CharField object at 0xb527388c>
<django.forms.fields.IntegerField object at 0xb52738ec>
<django.forms.fields.ChoiceField object at 0xb527394c>
I have read that these are not the same as BoundField object. How to render fields or is there any other better approaches to group fields?
If you do not want use any additional libraries, then the most easy solution is to render them manually, i would say. Otherwise you will just spend alot of time repeating the functionality of the library i copied as comment to your post.
There is always the case that things should be DRY. But we build websites for the users and user cares little about how the form rendering in template is done. For this reason we have often created form templates manually like this:
<div class="something">
{{ form.fieldname.label_tag }}{{ form.fieldname }}
</div>
Easyest way to organise it saving you some time. And in my opinion it is not that bad either, since this is not very common when you need fields organised by fieldsets.
I know this question is rather old, but I am sure there are still people who can benefit from a simple solution:
Say you have a group name and list of members. You can define a self.fieldset in your form's init to be a dictionary of {'group_1': ['member_1', 'member_2', ... ], ... }. Once you attach this to the form, you can pass it to views and from there to the template:
In forms.py:
class MyForm:
def __init__(self, current_user, *args, **kwargs):
super(YourForm, self).__init__(*args, **kwargs)
self.field['group'].queryset = Group.objects.filter(user = current_user)
...
In views.py:
form = self.Form(current_user)
the_fieldsets = form.fieldset
c = {'form': search_form,
'fieldsets': the_fieldsets }
In your template:
{% for field in form %}
<tr>
<td>{{field.label_tag}}</td>
{% if field.name == 'group' %}
<td>
<select id='{{field.id}}' name='{{field.name}}'>
{% for k,v in fieldsets.items %}
<optgroup label = {{k.name}}>
{% for val in v %}
<option name='{{val}} value = {{val.id}}> {{val.name}} </option> # Note that the select needs to return 'id', so value has to be {{val.id}}
{% endfor %}
</optgroup>
{% endfor %}
</select>
</td>
{% else %}
<td>{{field}}</td>
{% endif %}
<td>{{field.help_text}}</td>
<td>{{field.errors}}</td>
</tr>
{% endfor %}

Render multiple quersets in html page in Django

views.py
def f_page(request, slug, f_slug):
f = get_object_or_404(F, slug= f_slug)
if f:
events = f.event_set.order_by('-pub_date')
if events:
for event in events:
comments = event.comment_set.order_by('-pub_date')
variables = RequestContext(request, {
'f' : f,
'events' : events,
'comments' : comments,
})
return render_to_response('f_page.html', variables)
Here events and comments are iterables. Each f has multiple events and then each event has multiple comments. The problem is how do I render this in my html page.I have tried this but it didn't work:
{% for event in events %}
{{ event.author.first_name }}
{{ event.pub_date }}
{{ event.description }}
{% for comment in comments %}
{{ comment }}
{{ comment.author.first_name }}
{% endfor %}
{% endfor %}
The events part shows up right but the comment set doesn't show up.
My models work just fine I have tried everything in the admin panel. I can see where I'm messing it up but am unable to solve it. In 'views.py' the comments object for each event is not being saved. But, I don't know how to resolve it.
Please help. Thanks in advance.
Instead of passing comments to template you can get it in template.
{% for event in events %}
{{ event.author.first_name }}
{{ event.pub_date }}
{{ event.description }}
{% for comment in event.comment_set.all %}
{{ comment }}
{{ comment.author.first_name }}
{% endfor %}
{% endfor %}
Add ordering field in your models Meta class so that event.comment_set.all gives you data in your order.
class Comment(Model):
...
class Meta:
ordering = ['-pub_date']

Categories