I have a class Explore, which is used to get all the photos of other classes (like Photo, ProfilePicture, etc). And in the Explore class I have a method (get_rendered_html) which is used to render the objects in the template (explore_photo.html), so that I can pass it on to the main page (index.html).
Update:
Please let me elaborate my question. Suppose there are three Users A, B and C. If A is the current user, I want all the photos of A to be displayed, including other PUBLIC photos of B and C.
I tried this:
{% if object.display == 'P' or user == object.user %}
{% if object.display == 'P' or request.user == object.user %}
If I do it this way, only the public images are displayed, even though the current user is the object's user. And if I do this way:
{% if object.display == 'P' or user != object.user %}
All the image are displayed.
What may be the cause here? Please help me solve this problem. I would really appreciate your suggestion or advice. Thank you.
class Photo(models.Model):
ONLYME = 'O'
FRIENDS = 'F'
PUBLIC = 'P'
CHOICES = (
(ONLYME, "Me"),
(FRIENDS, "Friends"),
(PUBLIC, "Public"),
)
display = models.CharField(default='F', max_length=1, choices=CHOICES, blank=True, null=True)
user = models.ForeignKey(User)
description = models.TextField()
pub_date = models.DateTimeField(auto_now=True, auto_now_add=False)
update = models.DateTimeField(auto_now=False, auto_now_add=True)
image = models.ImageField(upload_to=get_upload_file_name, blank=True)
class Meta:
ordering = ['-pub_date']
verbose_name = 'photo'
verbose_name_plural = 'photos'
def __unicode__(self):
return self.description
class Explore(models.Model):
user = models.ForeignKey(User)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
pub_date = models.DateTimeField()
content_object = generic.GenericForeignKey('content_type','object_id')
def get_rendered_html(self):
template_name = 'explore_photo.html'
return mark_safe(render_to_string(template_name, {'object': self.content_object}))
explore_photo.html:
{% if object.display == 'P' or user == object.user %}
<div class="explore_photo">
<img src="media/{{ object.image }}">
<p class="photo_date">{{ object.pub_date|date:"F jS Y, P" }}</p>
<p class="photo_description">{{object.description}}</p>
<p class="photo_user">{{ object.user }}</p>
</div>
{% endif %}
index.html:
<body>
<h1>Explore</h1>
<div id="explore">
{% for photo in photos %}
<p>1</p>
{{ photo.get_rendered_html }}
<hr>
{% endfor %}
</div>
</body>
Update:
def explore(request):
"""
Return all the photos of the Explore Class in the Explore url
"""
photos = Explore.objects.all()
return render(request, 'index.html', {'photos':photos})
Disclaimer: I'm not familiar with GenericForeignKey and I don't understand why you have a user FK in Explore. I'm sure of one thing though: the filtering should be done in the view, not in the template! Use a something like that:
photos = Explore.objects.filter(content_object__display='P',
content_object__user=request.user)
# But as I said, I don't understand this Explore model, so I would probably
# delete it and use Photo directly
photos = Photo.objects.filter(display='P', user=request.user)
Now you can remove the condition in your template and it should work.
EDIT
My bad, you need a OR query, not a AND. Use Q objects in the filter instead.
from django.db.models import Q
photos = Photo.objects.filter(Q(display='P') | Q(user=request.user))
# Idem for the Explore model or any other model where you need a OR or a
# complex query.
You might want to compare User.pk rather than User objects.
Edit :
In this line : {% if object.display == 'P' or user == object.user %} and the others involving user, object.user or request.user, django will not guarantee that the objects will be equal, even if they should be. To compare an object against another, you want to compare some values they hold, comparing primary keys (user.pk) is a convention.
{% if object.display == 'P' or user.pk == object.user.pk %} should work fine. (well, i honestly suppose it will)
Related
I have a website that has some services that has to be subscribed individually. The user has to subscribe the services he wants to avail. Now there are three plans for subscription monthly, quaterly and half-yearly. The user has to choose one of them individually for each service he is opting for.
For example, I have three services civil, tax, criminal. Now a user can choose to subscribe Civil for 1 month and Criminal for 6 month hence these will expire too individually. Based on the services he has choosen he will get menus once he logs in. Below is the approach I took to make the models.
models.py
SUB_CHOICES = (
('monthly', 'monthly'),
('quaterly', 'quaterly'),
('hf', 'half_yearly'),
('anually', 'anually'),
)
class ServiceProduct(models.Model):
title = models.CharField(max_length=50, null = True)
code = models.IntegerField(null=True, unique= True)
price = models.DecimalField(max_digits=10,decimal_places=2,null=True)
def __str__(self):
return self.title
class UserSubscription(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
service_choosen = models.ForeignKey(ServiceProduct, on_delete=models.CASCADE, null=True)
is_active = models.BooleanField(default= False)
subscribed_on = models.DateTimeField(null=True)
expiring_on = models.DateTimeField(null=True)
Here i connected the usermembership model to customuser with foreign key to make multiple subscription for the same user having is_active class that shall toggle based on the subscription dates and expiring dates. The ServiceProduct models stores the name, prices etc of the service that would be required during checkout
But now as I said the user will get menus after login based on what services is active in subscription I am unable to do this in the template.
I passed the value from my views as
views.py
subuser = request.user
sub = UserSubscription.objects.filter(user = subuser)
return render(request, 'testpage.html', {'subuser': subuser, 'sub': sub})
But in html i cannot write somthing like
{% for cust in sub %}
{% if cust.service_choosen.civil.is_active %}
do something...
{% endif %}
{% endfor %}
I am sure that I am unable to make the correct models .Therefore please suggest me what will be the right approach to create models that would make this type of subscription method possible.
Desired result:
If the user chooses civil for a month and criminal for 6 months then Civil should be active for him for a month and Criminal should be active for him for 6 months
I believe that is_active would be better as model method. Like this:
def is_active(self):
return timezone.now() <= self.expiring_on
because it will "update" itself automatically if the subsrciption time will pass.
Share more views.py and I will tell you, how to pass context to html.
in views.py I would first filter UserSubscription to select only active subscriptions
subuser = request.user
sub = UserSubscription.objects.filter(user = subuser, is_active = True)
return render(request, 'testpage.html', {'subuser': subuser, 'sub': sub})
In this way you can simplify your template:
{% for cust in sub %}
{% if cust.service_choosen.title == 'civil' %}
do something with Civil...
{% elif cust.service_choosen.title == 'tax' %}
do something with Tax...
{% elif cust.service_choosen.title == 'criminal' %}
do something with Criminal...
{% endif %}
{% endfor %}
or using code instead of title (suppose codes 1=Civil, 2=Tax and so on):
{% for cust in sub %}
{% if cust.service_choosen.code == 1 %}
do something with Civil...
{% elif cust.service_choosen.code == 2 %}
do something with Tax...
{% elif cust.service_choosen.code == 3 %}
do something with Criminal...
{% endif %}
{% endfor %}
Depending on the output you need, you could also try to generalize (example)
{% for cust in sub %}
<a href='#'>Service {{ cust.service_choosen.title }}</a>
{% endfor %}
Also consider that calculating is_active on the fly like suggested by NixonSparrow answer is a best practice
I apologize in advance if this has already been asked, but I couldn't find any answers that answered the problem I'm having:
I need to do something similar to a For...Else loop in a Django template.
I need to show a button on a template base on an if condition:
If the user has already bought this product, then show button 1
If the user has not bought this product, then show button 2
For each product, I have to go through the user's purchases, and then show one button or the other depending on whether they have already bought the product or not.
A simplified (and wrong) version of the code would be like:
{% for product in products %}
//other code here
{% for purchase in purchases %}
{% if purchase.service_id.id == product.id %}
// show button 1
{% else %}
// show button 2
{% endif %}
{% endfor %}
{% endfor %}
However, this code doesn't work, as it shows both buttons as it goes through the for loop.
I can't do a For...Empty because the user may have other purchases (so the for loop wouldn't be empty), but none of them coincide with this product.
Thanks in advance.
EDIT:
Thanks #JashOFHop for the reply! In the end, I found a way around. I'll share it in case anyone else find themselves in this situation:
For clarity, the models concerned in this case are:
class User(AbstractUser):
pass
class Service(models.Model):
user_id = models.ForeignKey("User", on_delete=models.CASCADE, related_name="user_id_services")
name = models.CharField(max_length=64)
status = models.BooleanField(default=True)
description = models.TextField(max_length=300)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="category_name")
price = models.IntegerField()
slots = models.IntegerField(default=1)
amount = models.IntegerField(default=1)
watchedby = models.ManyToManyField(User, blank=True, related_name="watchedby")
class Purchase(models.Model):
user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="user_id_purchases")
service_id = models.ForeignKey(Service, on_delete=models.CASCADE, related_name="service_id_purchases")
amountpaid = models.IntegerField()
The view for this template was:
def explore(request):
# get all the active services from the db
services = Service.objects.filter(status=True).order_by('name')
# get the catogories for the filter option
categories = Category.objects.all().order_by('category')
# get the info of the user
userinfo = User.objects.get(username=request.user)
# get the info of the user's purchases
purchases = Purchase.objects.filter(user_id=userinfo)
# render the template
return render(request, "barter/explore.html", {
"services": services,
"categories": categories,
"userinfo": userinfo,
"purchases": purchases
})
And the template, as explained above, rendered all the services and in each is supposed to check whether this user has already bought said service or not.
Solution:
In the view I've added this and passed it also to the template:
# create a list of the IDs of the services purchased by the user to be able to render the buy/bought button correctly
purchases_list = []
for purchase in purchases:
purchases_list.append(purchase.service_id.id)
Then, the template is:
{% for service in services %}
// other code with infomation of the service here
// Important part:
{% if service.id in purchases_list %}
<button>You already bought this service</button>
{% else %}
<button>Buy now</button>
{% endif %}
{% endfor %}
You can create another models, for example Profile
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
cart = models.MenyToManyField(Product, null=True)
so in views for byu button you can write like this
def buy(request, pk): #byu button
product = Product.objects.get(id=pk)
profile = Profile.objects.filter(user.username=request.user)
profile.cart.add(product.id)
return redirect('some html')
another button has the same
def index(request, pk): # for template
product = Product.objects.get(id=pk)
profile = Profile.objects.filter(user.username=request.user)
return response(request, 'some html', context)
so template
{% for i in product%}
{% for products in profile%}
{% for p in products.cart.all %}
{% if i.id in p.id%}
<p>you bought</p>
{% else %}
<a href='byu'>Buy</a>
{% endif %}
{% endfor%}
{% endfor%}
{% endfor%}
but i think there is another the easiest way to solve your problem
Intro: I have a 3 models user, post, group. User is able to make posts however each post has to belong to a group. I have 400 fixed groups. Users have to choose from the existing 400 groups for their posts. Users cannot add, delete, update group's.
Furthermore:
Users can become a member of groups and when they click on a certain group. They see all the posts in that group.
Users can follow-unfollow other users.
**What I have right now:**When a user signs-in. In his home page he sees. All the posts of the each individual group he is a member of. When all the posts from all the groups are done with. He then sees the posts of all the people he follows one by one
What I want: I want the posts to be arranged by time
Example: If one of the people he follows just wrote a post then that post is first. Chronologically the second post was in one of the groups that he is a member of. That post comes second... You get the idea
Below are my models
class Post(models.Model):
user = models.ForeignKey(User, related_name='posts')
group = models.ForeignKey(Group, related_name='posts')
title = models.CharField(max_length=250, unique=True)
message = models.TextField()
created_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
class Group(models.Model):
name = models.CharField(max_length=250, unique=True)
members = models.ManyToManyField(User, through='GroupMember')
my views.py
class Homepage(TemplateView):
template_name = 'home.html'
def get_context_data(self, **kwargs):
context = super(Homepage, self).get_context_data(**kwargs)
posts_of_people_i_follow = []
posts_from_groups_that_i_member = []
if self.request.user.is_authenticated():
my = self.request.user
for user in my.following.all():
posts_of_people_i_follow += user.posts.all()
posts_of_people_i_follow.save()
for group in my.group_set.all():
posts_from_groups_that_i_member += group.posts.all()
posts_from_groups_that_i_member.save()
context['posts_of_people_I_follow_list'] = posts_of_people_i_follow
context['posts_from_groups_that_i_member'] = posts_from_groups_that_i_member
return context
In my Templates I currently have
{% for post in posts_from_groups_that_i_member %}
{{ post.title }}
{{ post.message }}
{% endfor %}
{% for post in posts_of_people_I_follow_list %}
{{ post.title }}
{{ post.message }}
{% endfor %}
Option 2: After breaking my head on this for a while I have been trying a different approach. But this looks really expensive
In the user profile model
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
def get_all_posts(self):
my = self.user
all_posts_for_this_user = []
for user in my.following.all():
all_posts_for_this_user += user.posts.all()
all_posts_for_this_user.save()
for group in my.group_set.all():
all_posts_for_this_user += group.posts.all()
all_posts_for_this_user.save()
return all_posts_for_this_user
Then in my templates
{% for post in user.profile.get_all_posts %}
{{ post.title }}
{{ post.message }}
{% endfor %}
Can anyone think of a better solution. Or is this ok
if I understand it correctly, you need post date in your Post model then you can order posts by Post.objects.ordey_by('post_date')
https://docs.djangoproject.com/en/2.1/ref/models/querysets/#django.db.models.query.QuerySet.order_by
Modify your view class by using an 'order_by()' method on the queryset that fetches your post objects and filters them according to when they were created (The 'created_at' field comes in handy here).
Add this to your 'Homepage' view class:
def get_queryset(self):
queryset_list = Post.objects.order_by('-created_at')
Further reading can be found here:
https://docs.djangoproject.com/en/2.1/ref/models/querysets/
EDIT
I think the template rendering issue deserves its own question?
In your template, try this:
{% for post in posts_from_groups_that_i_member %}
{% if post in posts_of_people_I_follow_list %}
{{ post.title }}
{{ post.message }}
{% endfor %}
{% endfor %}
In terms of performance, it may not be the best option but it should work. Let me know your findings.
I am willing to use Django for a school project but I'm encountering several issues.
The one I need help for is described in the title. Basically, I have a todo application in which I could add tasks. Now that I added a form in a my view to let the user add a task, I can't access the tasks in the Django admin.
I can still delete them with the admin but each time I try to add or to edit a task through the admin it throws me this error :
TypeError at /admin/todo/task/12/`
render_option() argument after * must be a sequence, not int
But, the form I added for the user works well.
My guess is that the 12 we can see the url is making the error but I don't know why. I point out that I'm still kinda new to Django, I didn't find any similar problem (found this but it didn't help me) so I thought it could be a good idea to ask here :). Here are my files :
todo/models.py
PRIORITY_TYPES = (
(1, 'Normal'),
(2, 'High'),
)
class Task(models.Model):
application = models.CharField(max_length=120, default='esportbets')
title = models.CharField(max_length=120)
author = models.CharField(max_length=60, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
completed = models.DateTimeField(blank=True, null=True)
priority = models.IntegerField(choices=[PRIORITY_TYPES], default=1)
done = models.BooleanField(default=False)
def __unicode__(self):
return self.title
todo/forms.py
class AddTaskForm(forms.Form):
application = forms.CharField(max_length=120, initial='esportbets', help_text='the application it is about')
title = forms.CharField(max_length=120, help_text='the task to do')
priority = forms.ChoiceField(choices=PRIORITY_TYPES, initial=1)
todo/views.py
def index(request):
if request.method == 'POST':
form = AddTaskForm(request.POST)
if form.is_valid():
new_task = Task.objects.create(application=form.cleaned_data['application'],
title=form.cleaned_data['title'],
priority=form.cleaned_data['priority'])
request.POST = None
redirect('/todo/', RequestContext(request))
else:
form = AddTaskForm()
tasks = Task.objects.all().order_by('-created')
tasks_high = tasks.filter(priority=2)
tasks_normal = tasks.filter(priority=1)
template_datas = {'form':form, 'tasks_high':tasks_high, 'tasks_normal':tasks_normal, 'user':request.user}
return render_to_response('todo/base.html', template_datas, RequestContext(request))
todo/base.html
{% if user.is_authenticated %}
<hr /><h3>ADD A TASK</h3><br />
<form method="post" action=".">
{% csrf_token %}
{{ form.as_p }}
<br />
<input type="submit" value="add" />
<input type="reset" value="reset" />
</form>
{% endif %}
todo/models.py: remove the [] around PRIORITY_TYPES.
todo/forms.py: replace the forms.ChoiceField(...) by forms.TypedChoiceField(choices=PRIORITY_TYPES, initial=1, coerce=int)
Since you are essentially copying data 1:1 from the form to a model, I'd recommend using django.forms.ModelForm.
If you want to minimize your code further you could use the generic CreateView. I recently wrote an answer to "Best practices on saving in a view, based on example code" which includes some example code.
I have 2 models in one app and 1 view. I'm currently pulling information from 1 model perfectly fine. However i wish to pull in another model from the same app and output them both to the same page.
The idea of the page is it being a a news hub so it's pulling through different types of news posts (from one model) and a different type of post which is from the other model.
I'm fairly new to Django so go easy! :) Anyway here is the code:
//VIEWS
def news_home(request):
page_context = details(request, path="news-hub", only_context=True)
recent_posts = NewsPost.objects.filter(live=True, case_study=False).order_by("-posted")[:5]
recent_posts_pages = Paginator(recent_posts, 100)
current_page = request.GET.get("page", 1)
this_page = recent_posts_pages.page(current_page)
notes = BriefingNote.objects.filter(live=True).order_by("-posted")
news_categories = NewsCategory.objects.all()
news_context = {
"recent_posts": this_page.object_list,
"news_categories": news_categories,
"pages": recent_posts_pages,
"note": notes,
}
context = dict(page_context)
context.update(news_context)
return render_to_response('news_hub_REDESIGN.html', context, context_instance=RequestContext(request))
//model 1
class BriefingNote(models.Model):
title = models.CharField(max_length=300)
thumbnail = models.ImageField(upload_to='images/briefing_notes', blank=True)
file = models.FileField(upload_to='files/briefing_notes')
live = models.BooleanField(help_text="The post will only show on the frontend if the 'live' box is checked")
categories = models.ManyToManyField("NewsCategory")
# Dates
posted = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __unicode__(self):
return u"%s" % self.title
// model 2
class NewsPost(models.Model):
title = models.CharField(max_length=400)
slug = models.SlugField(help_text="This will form the URL of the post")
summary = models.TextField(help_text="To be used on the listings pages. Any formatting here will be ignored on the listings page.")
post = models.TextField(blank=True)
#TO BE REMOVED????
thumbnail = models.ImageField(help_text="To be displayed on listings pages", upload_to="images/news", blank=True)
remove_thumbnail = models.BooleanField()
I'm outputting the content on the front end like so:
{% for post in recent_posts %}
<div class='news_first'>
<img class="news_thumb" src="/media/{% if post.article_type %}{{post.article_type.image}}{% endif %}{% if post.news_type %}{{post.news_type.image}}{% endif%}" alt="">
<h3><a href='{{post.get_absolute_url}}'>{% if post.article_type.title %}{{post.title}}{% endif %} <span>{{post.posted|date:"d/m/y"}}</span></a></h3>
<p class='news_summary'>
{% if post.thumbnail %}<a href='{{post.get_absolute_url}}'><img src='{% thumbnail post.thumbnail 120x100 crop upscale %}' alt='{{post.title}}' class='news_thumbnail'/></a>{% endif %}{{post.summary|striptags}} <a href='{{post.get_absolute_url}}'>Read full story ยป</a>
</p>
<div class='clearboth'></div>
</div>
{% endfor %}
I was thinking perhaps i could output them both within the same forloop however they need to ordered by -posted. So i though this could mess things up.
If you need anymore info please let me know.
Thanks in advance.
I've now solved the problem.
news_hub = list(chain(notes, recent_posts))
news_hub = sorted(
chain(notes, recent_posts),
key = attrgetter('posted'), reverse=True)[:10]