Django, filter by multiple values - django

So I have these models:
Bands(models.Model):
mgmt = models.ForeignKey(User)
name = models.Charfield(max_length=200)
Contracts(models.Model):
band = models.ForeignKey(Bands)
start_date= models.DateField()
BookedGig(models.Model):
for = models.ForeignKey(Bands)
under= models.ForeignKey(Contracts)
date = models.DateField()
How would I construct something in my views.py file to capture all the BookedGigs for a user? My goal is just to display through a template, the various gigs under the title of the
contacts/bands.
in views.py I currently have
def Home(request):
user = request.user
bands = Bands.objects.filter(mgmt=user).order_by('name')
#This will give me the bands belonging to a user
contracts = Contracts.filter(band=bands)
#But here bands is not one value but a queryset.
#if I try
contracts = bands.booked_gig_set.all()
I get 'QuerySet' object has no attribute 'booked_gig_set'
templates: I know this is wrong but this is how I'd like to display the lists.
{% for b in bands %}
Band:{{b.name}}
{% for c in contracts %}
Contract Start:{{c.start_date}}
{% for g in gigs %}
{{g.dates}}
{% endfor %}
{% endfor %}
{% endfor %}
Thanks

Contracts.objects.filter(band__in=bands)
You might want to add a prefetch_related statement there to prefetch the gigs though, otherwise your template loop will hit the DB once per contract.
contracts = Contracts.objects.filter(band__in=bands).prefetch_related()

Related

How to make individual product subscription for the same user in django

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

How To: For...Else in a Django template (if/else inside a for loop)

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

How to arrange posts from different models in Order of time when they were created

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.

Filtering field in DJango in the URL and in the view

Lets say I have a model with 2 fields:
class Bands(models.Model):
id = models.IntegerField(db_column='ID', primary_key=True)
name = models.CharField(db_column='NAME')
type = models.CharField(db_column='TYPE')
...
What I want to do is to list all the fields data with just one template. For example:
{% block content %}
{{ field_name }}
<ul>
{% for band in bands %}
<li>{{ band }}</li>
{% endfor %}
</ul>
{% endblock %}
So, how should I make my url?
url(r'^band/$', views.band, name='band')
or
url(r'^band/(?P<band>\w+)/$', views.band, name='band')
The link to that page would be:
Name
In the view I'm taking the values as this:
def band(request, field):
results = Results.objects.all()
names = [n.name for n in results]
types = [t.type for t in results]
if field == 'name':
bands = names
else:
bands = types
return render(request, 'band.html', {'bands': bands, 'field_name': field})
Is this the right way to do this (in the view and the url)? Thanks in advance.
Well, the simplest thing to do is use the DetailView.
from .models import Band
class BandDetailView(DetailView):
model = Band
And in urls.py something like:
from band.views import BandDetailView
url(r'^band/(?P<pk>\d+)/?$', BandDetailView.as_view(), name='band-detail')
And in the template:
{% url 'band-detail' pk=1 %}
That said, your model doesn't make much sense to me, as does the Led Zeppelin vs. Deep Purple bits in the view. Can you explain your project / need a bit more?

Django question: how can I get user specific information related to a domain object?

Here are my models:
class Activity(models.Model):
title = models.CharField(blank=False, max_length=100)
description = models.TextField(blank=False)
class UserActivityWork(models.Model):
activity = models.ForeignKey(Activity)
user = models.ForeignKey(User)
hours_worked = models.FloatField()
comment = models.TextField()
Example data would be, an Activity of "climbing Mt Everest" and each user would be able to input how long it took them and a comment.
Here's my question: How can I display a list of all the Activities, and if the user has entered data for that Activity, display the pertinent details next to the Activity?
So far, I have considered:
creating a dictionary of
UserActivityWork with a key of the Activity id and a value of the user's UserActivityWork. This would be fine with
me, but I have no idea of how to do
this in django's templating system (ie, how do you say: {{ user_work[activity.id] }})
creating an object that would hold
both the Activity and
UserActivityWork. I haven't done this
one, because I am hoping that django
has a better way to do this.
Any insight would be greatly appreciated!
Assuming you have 2 querysets accessable from within your template (say as activities and user_activities)
A naive way would be to iterate over each activity and then over each user activity.
{% for activity in activities %}
{{ activity.title }}
{% for user_activity in user_activities %}
{% ifequal user_activity.activity activity %}
Display userdata
{% endifequal %}
{% endfor %}
{% endfor %}
Dictionary lookups can be performed in templates by using a dot (.)
Technically, when the template system encounters a dot, it tries the following lookups, in this order:
Dictionary lookup
Attribute lookup
Method call
List-index lookup
Another option would be to create a custom template tag. You could loop over the activity list as before and then pass the activity and either the user_activity list or the user to the tag to perform the lookup and render the required data.
Thanks for the hint, Gerry. I found that writing a custom template tag as you suggested was the way to go.
Here are the gory details, in case anyone stumbles across this.
In the view method, I published a dictionary "user_activity_status" which contains a key of activity.id and value of UserActivityWork object for the logged in user's work on that activity
This is the the relevant section of the template. Basically this going to add a variable "map_value" with a value of
getattr(user_activity_status[activity.id], "comment")
Here's the template:
{% load *file-name-of-the-templatetag-file* %}
{% access_map_method user_activity_status activity.id comment %}
{% if map_value %}
{{ map_value }}
{% else %}
get working sucka!
{% endif %}
here is the section of the templatetag file (see Gerry's links for the details of how to set this up)
from django import template
register = template.Library()
#register.tag(name="access_map_method")
def do_access_map_method(parser, token):
try:
tag_name, dict_name , key_name, method_name = token.contents.split()
except ValueError:
msg = '%r tag requires three arguments' % token.contents[0]
raise template.TemplateSyntaxError(msg)
return MapNode(dict_name , key_name, method_name)
class MapNode(template.Node):
def __init__(self, dict_name, key_name, method_name):
self.dict_var = template.Variable(dict_name)
self.key_var = template.Variable(key_name)
self.method_name = method_name
def render(self, context):
try:
dict_obj = self.dict_var.resolve(context)
key_obj = self.key_var.resolve(context)
if key_obj in dict_obj.keys():
if self.method_name:
context['map_value'] = getattr(dict_obj[key_obj], self.method_name)
else:
context['map_value'] = dict_obj[key_obj]
else:
context['map_value'] = ''
except template.VariableDoesNotExist:
context['map_value'] = ''
return ''