Django - Prepare objects from a view for Current User - python

Consider this model
class Exercise(models.Model):
name = models.CharField(max_length=50)
def __unicode__(self):
return self.name
class Score(models.Model):
"""Scores of users by exercise"""
exo = models.ForeignKey(Exercise)
user = models.ForeignKey(User)
score = models.IntegerField()
class Meta:
unique_together = (('exo', 'user',),)
I have a template which displays the Exercises.
<ul>
{% for exo in exos %}
<li>{{ exo }}</li>
{% endfor %}
</ul>
Here is the view
def view_exos(request):
"""Lists Exercises"""
objs = {
'exos': Exercise.objects.all(),
}
return render_to_response('content/contents.html', objs
, context_instance=RequestContext(request)
)
Now I'd like to display the Score of the current user in front of each Exercise (if there is one) in order to access it from the template in this manner:
<li>{{ exo }} - {{ exo.user_score }}</li>

What I'd do would be to get all the user's current scores up front, create a dictionary mapping exercise to score, then add the score as an attribute of each exercise. Something like:
user_scores = request.user.score_set.all()
score_dict = dict((sc.exo_id, sc.score) for sc in user_scores)
exos = Exercise.objects.all()
for ex in exos:
ex.current_user_score = score_dict.get(ex.id)
Now each exercise in exos has a current_user_score attribute, which is the current user's score for that exercise (or None).

django.contrib.auth has a context processor that adds a user variable to the template context, referencing the current user. This can enable you to get all scores for the current user, then you can create a template filter that returns the score for a particular exercise.
In a file named exercises.py within a templatetags package.
[Put the package in the folder of one of your apps in INSTALLED_APPS. Remember templatetags must be a valid Python package ie. with an __init__.py]
from django.template import Library
register = Library()
#register.filter
def score_for_exercise(scores, exercise):
s = scores.filter(exo=exercise)
if s:
return s[0].score
return None
In the template:
{% load exercises %}
{% with user.score_set.all as user_scores %}
<ul>
{% for exo in exos %}
{% with user_scores|score_for_exercise:exo as score %}
<li>{{ exo }}{% if score %} - {{score}}{% endif %}</li>
{% endwith %}
{% endfor %}
</ul>
{% endwith %}

Maybe you can add an attribute to your Exercise:
class Exercise(models.Model):
name = models.CharField(max_length=50)
def __unicode__(self):
return self.name
def user_score(self):
return Score.objects.get(exo=self).score

Related

no such column: student_student.course_id

I am creating a simple system using Django Sqlite but I am facing this error whenever i try to open the table as an admin in the django/admin by clicking Studends table. The error is the following:
OperationalError at /admin/student/student/
no such column: student_student.course_id
I searched allot but could not find any exact solution to my problem.
The following is my code.
views.py
from django.shortcuts import render
from .models import Student
# Create your views here.
def index(request):
return render(request, "student/index.html",{
"student": Student.objects.all()
})
index.html
{% extends "student/layou.html" %}
{% block body %}
<h2>Student information</h2>
<ul>
{% for student in Student %}
<li> {{ student.id }} Student Full Name: {{ student.f_name }}{{ student.l_name }} in {{ student.grade }} with {{ student.gpa }} in {{ student.course_id }}</li>
{% empty %}
No information entered
{% endfor %}
</ul>
{% endblock %}
models.py
from django.db import models
# Create your models here.
class Course(models.Model):
code = models.CharField(max_length=10)
course_name = models.CharField(max_length=64)
def __str__(self):
return f"Course name: {self.course_name} with course code ({self.code})"
class Student(models.Model):
f_name = models.CharField(max_length=64)
l_name = models.CharField(max_length=64)
course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name= "Classes" )
grade = models.CharField(max_length=10)
gpa = models.DecimalField(max_digits=4.0, max_length=4, decimal_places=2)
def __str__(self):
return f"{self.id} Full Name: {self.f_name} {self.l_name} in {self.grade} with a gpa {self.gpa} in course {self.course_id}"
You cannot refer to Course object via Student object with that:
{{ student.course_id }}
You can get to object or it's id like that:
{{ student.course }} # returns related Course object
{{ student.course.id }} # returns related Course object's id
For future reference, you also want to make more changes:
"student": Student.objects.all()
# change to:
"students": Student.objects.all()
{% for student in Student %}
# change to:
{% for student in students %}
{% extends "student/layou.html" %}
# probably change to:
{% extends "student/layout.html" %}

Django. Why would my foreign key does not match the same data from parent primary key

I have 2 models:
class Director(models.Model):
director_name = models.TextField(primary_key=True)
director_firstname = models.CharField(max_length=32)
def __str__(self):
return f'{self.director_name}'
def get_absolute_url(self):
return reverse('director-detail', args=[str(self.director_name)])
class Meta:
managed = False
db_table = 'director'
ordering = ['director_name']
class Connection(models.Model):
director_connect = models.ForeignKey('Director', models.DO_NOTHING, db_column='director_connect')
worker_connect = models.ForeignKey('Worker', models.DO_NOTHING, db_column='worker_connect')
class Meta:
managed = False
db_table = 'connection'
unique_together = (('director_connect', 'worker_connect'),)
ordering = ['director_connect']
def __str__(self):
return f'{self.director_connect}'
def get_absolute_url(self):
return reverse('director-detail', args=[str(self.director_connect)])
This is my view.py file:
class DirectorDetailView(generic.DetailView):
model=Director
template_name = 'company/director_detail.html'
def get_context_data(self, **qwargs):
a = super(DirectorDetailView, self).get_context_data(**qwargs)
a['cons'] = Connection.objects.all()
return a
When I am trying to match 2 columns in html with for loop and if statement, they do not match, although they are similar one to one copies of each other:
{% extends "index.html" %}
{% block content %}
<h1>{{ director.director_name }}</h1>
<p>{{ director.firstname }}</p>
{% for con in cons %}
{% if object.director_name == con.director_connect %}
<li>{{con.id}}, {{con.director_connect}}, {{con.worker_connect}}</li>
{% endif %}
{% endfor %}
{% endblock %}
How could I fix it? I would like to bring the list of workers under director's name. Any link to probable answer (I was looking for it, but I couldn't find it) or slight push towards the right direction would be nice.
The issue is that in your template you're comparing the director's name with the primary key that's referenced in the Connection by means of the director_connect ForeignKey. The simplest solution would be to change the if expression:
{% extends "index.html" %}
{% block content %}
<h1>{{ director.director_name }}</h1>
<p>{{ director.firstname }}</p>
{% for con in cons %}
{% if object == con.director_connect %} // Change it so it compares the primary key
<li>{{con.id}}, {{con.director_connect}}, {{con.worker_connect}}</li>
{% endif %}
{% endfor %}
{% endblock %}
Other way to achieve this would be to filter the workers by director in the view, using filter:
class DirectorDetailView(generic.DetailView):
model=Director
template_name = 'company/director_detail.html'
def get_context_data(self, **qwargs):
a = super(DirectorDetailView, self).get_context_data(**qwargs)
director = self.get_object()
a['cons'] = Connection.objets.filter(direct_connect=director).all()
return a
This is better because it will filter the workers in the database using SQL which is probably faster than the template engine.
Other solution would be to use a ManyToManyField and let django ORM take care of the relations for you, but that would require refactoring the code and maybe some database migrations.

Django views is it possible to select only the first four elements for a specific model.item?

I have a profile model that is displaying various items.
One of those is the country attached to the profile.
Here is what happen in the view:
class ProfilePartnerListView(FormMixin, BaseProfilePartnerView, ListView):
model = ProfilePartner
context_object_name = 'profile_list'
view_url_name = 'djangocms_partner_profile:profile-list'
def get(self, request, *args, **kwargs):
context = {}
self.object_list = self.get_queryset().order_by('-date_created')
context.update(self.get_context_data(**kwargs))
context[self.context_object_name] = context['object_list']
country_for_articles = Country.objects.exclude(regions_partner_profile=None).order_by('name')
industries_qs = ProfilePartnerIndustry.objects.active_translations(
get_language()).order_by('translations__name')
budget_qs = ProfilePartner.objects.values_list('budget',
flat=True).distinct()
context['load_more_url'] = self.get_load_more_url(request, context)
context['regions_list'] = country_for_articles
context['industry_list'] = industries_qs
context['budget_list'] = budget_qs
return self.render_to_response(context)
I know, for example 'regions_list', how to return only 4 elements from it.
But the thing is, my main object 'profile_list' that I use in the template for the rendering, is displaying all the country of the item when I do:
{% for profile in profile_list %}
{% for country in profile.regions.all %}
<div class="col-xs-12">{{ country }}</div>
{% endfor %}
{% endfor %}
And some of the profiles got 5 or 6 country. I want to only display the first 4.
Is there a way of doing it?
Many thanks!
ps: region_list, industry_list and budget_list are use for categories, it has nothing to do with what I want here.
You could use slice filter for this:
{% for profile in profile_list %}
{% for country in profile.regions.all|slice:":4" %}
<div class="col-xs-12">{{ country }}</div>
{% endfor %}
{% endfor %}

Foreign key relation in Django template

I know this question is asked before many times but I still can't solve it.
model.py
class Awb (models.Model):
awb_id = models.CharField(primary_key=True, max_length=50)
awb_shipment_date = models.DateTimeField()
awb_shipper = models.CharField(max_length=250)
awb_sender_contact = models.CharField(max_length= 50)
class History (models.Model):
history_id = models.AutoField(primary_key=True)
awb = models.ForeignKey(Awb)
history_city_hub = models.CharField(max_length=250)
history_name_receiver = models.CharField(max_length=250)
view.py
def awb_list_view(request):
data = {}
data['awb'] = Awb.objects.all()
data['history'] = History.objects.all()
return render(request, 'portal/awb-list.html', data)
templates
{% for s in awb.history_set.all %}
{{ s.awb_id }}
{{ s.history_id }}
{% endfor %}
When I tried it with this code, there is no results in templates. I want to show awb_id and history_id in templates. Could you help me?
First let's take a look at the view code...
def awb_list_view(request):
data = {}
data['awb'] = Awb.objects.all()
data['history'] = History.objects.all()
return render(request, 'portal/awb-list.html', data)
The context dictionary being passed to the template contains an item with key 'awb' and respective QuerySet Awb.objects.all().
Now let's take a look at the template for loop...
{% for s in awb.history_set.all %}
This opening for loop template tag is trying to produce a reverse set of History objects. In order to achieve this, we would need a single AWB object instance. Instead, the 'awb' variable is a QuerySet which was passed as context to the template.
If the goal of this code is to show all AWB objects with their related History objects, the following template code should be valid.
{% for awb_obj in awb %}
{% for history_obj in awb_obj.history_set.all %}
{{ awb_obj.id }}
{{ history_obj.id }}
{% endfor %}
{% endfor %}
The Awb.history_set.all only applies to one Awb object, not a queryset.
This would work:
data['awb'] = Awb.objects.first() # If the first one has history
or:
Loop through all the Awb objects in the template to access the history_set for each one.
{% for a in awb %}
awb: {{ a.awb_id }}<br>
{% for h in a.history_set.all %}
history: {{ h.history_id }}<br>
{% endfor %}
{% endfor %}

Attempting to display total amount_won for each user in database via For loop

I'm trying to display the Sum of amount_won for each user_name in the database. My database is:
Stakes table
id
player_id
stakes
amount_won
last_play_date
Player table
id
user_name
real_name
site_played
models.py
class Player(models.Model):
user_name = models.CharField(max_length=200)
real_name = models.CharField(max_length=200)
SITE_CHOICES = (
('FTP', 'Full Tilt Poker'),
('Stars', 'Pokerstars'),
('UB', 'Ultimate Bet'),
)
site_played = models.CharField(max_length=5, choices=SITE_CHOICES)
def __unicode__(self):
return self.user_name
def was_created_today(self):
return self.pub_date.date() == datetime.date.today()
class Stakes(models.Model):
player = models.ForeignKey(Player)
stakes = models.CharField(max_length=200)
amount_won = models.DecimalField(max_digits=12, decimal_places=2)
last_play_date = models.DateTimeField('Date Last Updated')
def __unicode__(self):
return self.stakes
class PlayerForm(ModelForm):
class Meta:
model = Player
class StakesForm(ModelForm):
class Meta:
model = Stakes
Views.py
def index(request):
latest_player_list = Player.objects.all().order_by('id')[:20]
total_amount_won = Stakes.objects.filter(player__user_name='test_username').aggregate(Sum('amount_won'))
return render_to_response('stakeme/index.html', {
'latest_player_list': latest_player_list,
'total_amount_won': total_amount_won
})
and index.html
<h1> Players </h1>
{% if latest_player_list %}
<ul>
{% for player in latest_player_list %}
<li>{{ player.user_name }} <br>Total Won: {{ total_amount_won }}
</li>
{% endfor %}
</ul>
<br>
{% else %}
<p>No players are available.</p>
{% endif %}
<h3>New Player</h3>
If I leave the views.py section as (player__user_name='test_username') it will display Amount Won: as follows Total Won: {'amount_won__sum': Decimal('4225.00')} using the test_username's amount_won (4225.00) for EVERY user name. Ideally, I'd like it to display Amount Won: for each user name in the for loop and display it as "Amount Won: 4225.00" only.
I'm starting to understand this is way over my head, but I've read the docs regarding the differences between aggregate and annotate and I can't wrap my head around it. I'm thinking my DB is not setup correctly to use annotate for this, but I obviously could be wrong.
Check out: https://docs.djangoproject.com/en/dev/topics/db/aggregation/
players = Player.objects.annotate(total_amount_won=Sum('stakes__amount_won'))
players[0].total_amount_won # This will return the 'total amount won' for the 0th player
So you could pass players to your template and loop over it.
EDIT
Your views.py would look like:
def index(request):
players = Player.objects.annotate(total_amount_won=Sum('stakes__amount_won'))
return render_to_response('stakeme/index.html', {'players': players,})
The template would look like:
<h1> Players </h1>
{% if players %}
<ul>
{% for player in players %}
<li>
{{ player.user_name }} <br>Total Won: {{ player.total_amount_won }}
</li>
{% endfor %}
</ul>
<br />
{% else %}
<p>No players are available.</p>
{% endif %}
<h3>New Player</h3>

Categories

Resources