I am building an application having to do with game achievements, and I want to display for each game, a page that lists the related achievements. That is done.
However, I also want each user to be able to see in that list which achievements they have completed.
My model is like this:
class Achievement(models.Model):
game = models.ForeignKey(Game)
...
class Game(models.Model):
...
class GameProfile(models.Model):
achievements = models.ManyToManyField(Achievement, through='Achieved')
...
class Achieved(models.Model):
profile = models.ForeignKey(GameProfile)
achievement = models.ForeignKey(Achievement)
curr_count = models.PositiveSmallIntegerField()
In the game template page, I get all the achievements for the current game through the relationship with the Achievement model (ie achievements = game.achievement_set.all). Then I do a {% for ach in achievements %} on them and output a <li> for each.
If a user is logged in, I also want to display the curr_count associated with the achievement. So I need a list of Achieved objects for the specific GameProfile (think of it as the current user).
The problem is that I cannot do {% if ach in achieved %} (where ach is an Achievement instance and achieved an Achieved QuerySet), obviously.
I also thought I could get a QuerySet with the Achievements the user has achieved, but that lacks the curr_count, which I want to display.
I think what I want is some kind of list or dictionary with the Achievements of the user against which I can test if a specific Achievement is a member, but which will also carry the curr_count variable. Another solution might be to somehow add curr_count as a field to a QuerySet of Achievement objects (which will be the ones the user has achieved).
Can you suggest how to accomplish this in django?
If your GameProfile model has a link to (auth.)User then you can generate the following queryset and send it to the template.
def achievements_by_user(request, *args, **kwargs):
user = request.user
queryset = Achieved.objects.filter(profile__user = user)
return render_to_response(...)
This queryset will filter all rows from Achieved based on the currently logged in user. If you also want to filter based on game (i.e. return all achievements for the currently logged in user for a specific game) then you can add another filter condition.
queryset = Achieved.objects.filter(profile__user = user,
achievement__game = <a game>)
Update
Apparently I misunderstood the question. Here is another stab at it.
#register.filter
def achievement_in(achieved, achievement):
count = achieved.filter(achievement = achievement).count()
return count > 0
# In template
{% for ach in achievements %}
{% if achieved|achievement_in:ach %}
...
{% else %}
...
{% endif %}
{% endfor %}
Related
I have the following structure of the project (models):
Company (based on Groups) <-- Products (foreignKey to Company) <-- Reviews (foreignKey to Products).
Inside template i want to give a user an opportunity to filter reviews by products, but when using django_filters it shows corrects queryset of reviews (related only to company's products) but in filter form dropdown options i can see all products of all companies (even those which are not presented on the page), for example for Company X i see on my page only Cactus and Dakimakura reviews, but in filter form i can select Sausage (but i shouldnt, because its product from another company).
For now it's all looks like this:
#View
def reviewsView(request):
context = {}
user = request.user
company = user.company
products_qset = ProductModel.objects.filter(company=company)
reviews_objects = ReviewModel.objects.filter(product__in=products_qset)
filter = ReviewFilter(request.GET, queryset=reviews_objects)
context['filter'] = filter
return render(request, 'companies/reviews.html', context)
#Filter
class ReviewFilter(django_filters.FilterSet):
class Meta:
model = ReviewModel
fields = [
'product',
'marketplace',
'operator',
'state'
]
#Template
<form action="" method="get">
{{ filter.form.as_p }}
<input type="submit" name="press me" id="">
</form>
<div class="review-list">
{% for i in filter %}
{{i.product}}, {{i.marketplace}} etc.
{% endfor %}
I've done quite a lot of research on django_filters docs and this question seems like duplicate, but i can't fully understand what and why is happening to init in this answer and how to correctly rewrike class ReviewFilter so filter instance would do something like this (in View):
filter = ReviewFilter(request.GET, queryset_to_dispplay=MyQueryset, queryset_to_display_filtering_options=MyQueryset). For now its surely happening because ReviewFilter Meta points to the all table (ReviewModel) objects, instead of filtered beforehands.
I also trying to apply pagination on this page (i cut this from question code for "shortness") and after i will be able to implement filtering i would like to merge those two somehow, but if what i want is not possible - please point me to the right direction (for example: 'you can do this by writing your very own filtering system using some js, no need to use django_filters' or 'with angular.js/view.js/somethingelse you can have it all from the box').
I have a foreign key and I'm using the related_name field like so:
class Pizza(models.Model):
...
restaurant = models.ForeignKey('Restaurant', related_name='pizzas_offered')
active = models.BooleanField(...)
...
Example from the view:
my_restaurant = get_object_or_404(Restaurant, pk=id)
In any template I can run something like my_restaurant.pizzas_offered.all to get all the pizzas belonging to a particular restaurant. However, I only want the pizzas that are active (active=True). Is there a way to retrieve this subset in the template, without having to pass a separate variable in the view? Please note that I always want to only show the active pizzas only so if I have to make a change in the model to make this happen, that is fine too.
NOTE: in the view I can simply pass my_restaurant.pizzas_offered.filter(active=True) but it returns an error when I use it in the template:
{% for details in my_restaurant.pizzas_offered.filter(active=True) %}
{{ details.name }}
{% endfor %}
It returns this error:
Could not parse the remainder: '(active=True)'
There are some reasons why I want to do this on template level and not in the view (main reason: I often loop through all the records in the database and not just one restaurant, so I can't just query for the one record). So my question is how to do this on template-level.
You need to create a Manager for your Pizza model and set the Meta.base_manager_name:
class PizzaManager(models.Manager):
def active(self):
return self.filter(status=True)
class Pizza(models.Model):
...
objects = PizzaManager()
class meta:
base_manager_name = 'objects'
Now you can use the method active in your template:
{% for details in my_restaurant.pizzas_offered.active %}
...
{% endfor %}
For more information you can read the documentation about Default and Base managers.
I have a list of users displaying in templates like the following.
{% for u in users_list %}
{{u.name}}
{% endif %}
Is there a way to rank two or more users at the top If I want to?
For instance with one user, when the current user visits that list, I can rank him in the top without a specific ordering by excluding me before sending the variable to template.
1) me
2) user2
3) user3
If you want to order specific objects by id at the top of a queryset, you can order on a conditional expression - for example, to put the current user at the top, and order other users by name:
from django.db.models import Case, When
from django.contrib.auth.models import User
users = User.objects.order_by(
Case(When(id=request.user.id, then=0), default=1),
'last_name',
'first_name'
)
To put multiple objects at the top, just adjust the When condition:
ids_at_top = [1, 2]
users = User.objects.order_by(
Case(When(id__in=ids_at_top, then=0), default=1))
I would also consider though whether you can achieve what you want via simpler means - for example you could get the current user, exclude them from the main queryset, and then display separately in the template, like this
# in the view
current_user = request.user
users = User.objects.exclude(id=current_user.id)
# in the template
{{ current_user.name }}
{% for u in users %}
{{ u.name }}
{% endif %}
In your view create a list of users that you want to display. In order to display the current user you can use request.user to get the current user and append them to the list followed by the rest of the users. In your template you would do
<ol type="1">
{% for u in users_list %}
<li>{{u.name}}<li>
{% endif %}
</ol>
should out what you are looking for
Update example
user_list = list(Users.objects.filter(name="Test")) #Or how ever you want to get the users.
user_list.insert(0,request.user)
if you wanted to get another individual you would just get the user using
another_person = User.objects.get(id=some_id)
user_list.insert(0,another_person)
Usign the queryset you can order your users_list based on the fields in the User. For this purpose you have the sort method in the queryset. In your case seems to me you need to add a field so you can sort by that field. For example you add a rank field default 0 and than add a value for the hightest rank.
The full example is defined here: https://docs.djangoproject.com/en/2.1/topics/auth/customizing/#a-full-example
In your CustomUserMdel just add a field rank = models.IntegerField(default=0)
Then in your view you need to sort by rank:
from django.contrib.auth import get_user_model
User = get_user_model()
# in your view...
user_list = User.objects.all().sort_by('rank')
I have 2 models called Valuation and Assessment. A valuation has many assessments (foreignkey relationship). Users can only create 1 assessment for each valuation.
This seems to be very simple but I can't wrap my head around it. I need to check if any of a valuation's existing assessments belongs to the request.user, how do I do that?
this doesn't work because assessment_set.all is a list. (assessments in this case is a list of assessments for the currently displayed valuation)
{% if request.user.assessment_set.all not in assessments %}
# Display "make an assessment" form
{% endif %}
So I think I'd need to loop over request.user.assessment_set.all and see if each of the user's assessments is in the assessments list, but I feel like that is very inefficient and there must be a better way. Advice?
Based on your description I assume you have the following model architecture(i have used related_name for the reverse relationships),
class Valuation(models.Model):
# fields
class Assessment(models.Model):
#fields
user = models.ManyToManyField(User, related_name='assessments')
valuation = models.ForeignKey(Valuation, related_name='assessments')
So if you want to limit the logged in user to create only 1 assessment for each valuation, then you present only those valuations that are not assessed.
views.py
unassessed_valuations = Valuation.objects.exclude(assessments__user=request.user)
template
{% for valuation in unassessed_valuations %}
valuation assessment form
{% endfor %}
I want to iterate over user matching entities to avoid grouping in helper array first.
How from query like this I can make User.stone_set to contain only ones that matched the filter?
users = User.objects.filter(payment__due_day__lte=datetime.today()+3)
Now each user should have only payment instances that mateched the filter, but users.payment_set is a manager not really related to it.
Clarification
Here is my model:
class Payment(models.Model):
owner = models.ForeignKey(User)
due_day = models.IntegerField()
now, query like this: User.objects.filter(payment__due_day=10) will give me users that have payment due day 10. Now iterating over those users objects I want it to have only those payment I queried for the first time (only those that have payment with due_day = 10), without quering single user again for their payments.
If I understand your question, in the end you want a queryset of Payment objects.
Then, start from that model, not User.
payments = Payment.objects.filter(due_day=10).select_related('owner').order_by('owner')
After that, you can iterate over the queryset and get the owner (user) of each payment without any extra query, because select_related will do a SQL join for you in the background.
The order_by clause is needed if you have multiple payments for each user and you need to show that. In your template you can use the regroup built-in template tag.
For example:
# In your view (EXTREMELY simplified)
def my_view(request):
payments = Payment.objects.filter(due_day=10).select_related('owner').order_by('owner')
return render_to_response('template.html', {'payments': payments})
# In your template
{% regroup payments by owner as payment_list %}
<ul>
{% for payment in payment_list %}
<li>{{ payment.grouper }} <!-- this is the owner -->
<ul>
{% for item in payment.list %}
<li>{{ item.due_day }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
If, instead, you want to achieve that in bare python, the best solution is to use itertools.groupby (which is, in fact, used by the regroup tag).
A quick and untested example:
from itertools import groupby
from operator import attrgetter
payments = Payment.objects.filter(due_day=10).select_related('owner').order_by('owner')
for owner, grouped_payments in groupby(payments, attrgetter('owner')):
do_stuff_with(owner)
do_some_other_stuff_with(list(grouped_payments))
Be sure to read the docs.