Order a django queryset with specific objects first - django

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')

Related

Django filter related_name subset in templates

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.

Django templates - Check if user already has created an object for another object

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 %}

How to set the default option for a select field using Jinja templates

I need to add default option value to a select field using Jinja templates.
form.py
class TeamForm(Form):
user = SelectField(u'Team Member')
views.py
class myview(request,id):
form = TeamForm(request.form)
members =db.session.query(teams).filter_by(t_id = id).all()
return render_template("members.html",form=form,members=members)
member.html
<table>
{% for member in members%}
<tr>
<td>{{ form.user(class_="form-control",value=user.id) }}</td>
</tr>
{% endfor %}
</table>
The assigned option value is not shown in the output.
I have to loop the select fields depending upon the members in team.
For example, if a team has 3 members, I will display the three select fields and auto select those three members.
You have two questions in play here:
To implement default values in your form, just use the default=(value, label) option for the SelectField class in form.py.
Assume that you want to default to Johnny as your team member and the numerical value of that option is 0. Then you can do something like this using default=(value, label):
class TeamForm(Form):
user = SelectField(u'Team Member', default=(0, "Johnny"))
Just pass in a tuple in the form (value,label). Your values can be int's, strings, whatever. (You're also missing a Submit button, which I assume is accidental.)
I'm not sure what you're doing in your views. You say, "Here I have to loop the select fields depending upon the members in team. For example if team having 3 members, I will display the three select field with auto selected by those three members." Are you saying that you just need to show these to the user in a drop-down field when you render the form, or are you having trouble actually displaying query results to the user when the template is rendered?
Because you have a form in play, I assume you will be submitting data to a server via a POST. A typical view pattern creates a blank form object and passes it to the user when the template is rendered via a GET request. When the user enters data and submits it, then it is sent via a POST request. I also noticed that you used a class for your views. I suggest using a decorator instead for your routing and have it call a function.
If, say, you have three options in play in your form and you want to show them to your user in a drop-down, then you'll want to implement something like this:
form.py:
# Sample Form class; choices can be overridden later.
class TeamForm(Form):
user = SelectField(u'Team Member', default=(0, "Johnny"), choices=[(0, "Johnny"), (1, "Sarah"), (2, "Bill")])
submit= SubmitField('Submit')
view.py:
#app.route('/team', methods=['GET','POST'])
def team_view(id):
user_selection = None
form = TeamForm()
# This code block will only execute on POST
if request.method == 'POST':
if form.validate_on_submit():
user_selection = form.user.data
form.user.data = ''
return redirect(url_for(some-other-page.html))
members =db.session.query(teams).filter_by(t_id = id).all()
# This next line will override the default choices in your form.
form.user.choices = [(member.id, member.name) for member in members]
return render_template("members.html",form=form)
member.html:
Option 1:
<!-- Assume you're using Flask-Bootstrap & WTForms -->
{% import "bootstrap/wtf.html" as wtf %}
<!-- Render your Team form; this is where you call to your form object -->
{{ wtf.quick_form(form) }}
Option 2:
<!-- Manually construct your template -->
<form method="POST" action="/team">
<div>{{ form.user }}</div>
<div>{{ form.submit() }}</div>
</form>
I found a solution that worked perfectly for me.
If you look inside the form object of type SelectField, you will see that Jinja will render as selected the field that is by default.Ex:
type = SelectField('Company Type', choices=type_choices, default=1)
In the example above Jinja will render a select whose option will have a selected value where value=1.
Checking the form object instantiated in my views, I verified that there are three fields to fulfill this condition:
form.type.data = "1" # ---> string
form.type.default = 1 # ---> int
form.type.object_data = 1 # ---> int
So, replacing these three values with data brought from database, inside views, I was able to create a dynamic default select in Jinja, without having to make any changes to my HTML or forms:
form = EnterpriseForm()
result = "query to database"
form.type.data = str(result.id_enterprise_type)
form.type.default = result.id_enterprise_type
form.type.object_data = result.id_enterprise_type
I hope this information helps others.

Django contraint filtered models to include set that matches first criteria

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.

Check whether object is in QuerySet of related objects

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 %}