I have those models:
class Question(models.Model):
description = models.CharField(max_length = 255)
class Quiz(models.Model):
name = models.CharField(max_length = 32)
questions = models.ManyToManyField(Question, related_name = 'questions')
Im interested to have a property where it returns the index value for the related quiz.
So when i do like this in my views:
def view_quiz(request,slug):
quiz = Quiz.objects.get(name = slug)
questions = quiz.questions.all()
return render(request = request, template_name = 'main/quiz.html', context = {'quiz': quiz,'questions': questions})
I would be able also to access the index of the question.
I was thinking to create a property to question model like this:
class Question(models.Model):
description = models.CharField(max_length = 255)
options = models.ManyToManyField(Option, related_name = 'options',default = None)
#property
def question_number(self):
return 'index of the related quiz'
But i could not figure out the code for that property so it would return the index of the related questions.
Any suggestions? Thanks
A simple way of doing this would be to inject each index into the questions returned by the queryset. This should be a model method on the Quiz, since two quizzes could share the same Question objects.
Note: The index of the question is order dependent, and your quiz.questions.all() queryset has unstable ordering right now – the database won't necessarily return the questions in the same order each time. To force stable ordering, I'm going to assume that the Question instances are ordered by name.
class Quiz(models.Model):
...
def ordered_questions(self):
questions = self.questions.order_by("name")
# You can change to zero based indexing using `start=0`
for index, question in enumerate(questions, start=1):
question.index = index # Inject the index into the question
# Use a yield statement here to keep the queryset lazy and efficient.
yield question
Then wherever you need to access each question's index, you can use the quiz.ordered_questions() method.
Related
I might be missing something simple here. And I simply lack the knowledge or some how-to.
I got two models, one is site, the other one is siteField and the most important one - siteFieldValue.
My idea is to create a django table (for site) that uses the values from siteFieldValue as a number in a row, for a specific site, under certain header. The problem is - each site can have 50s of them. That * number of columns specified by def render_ functions * number of sites equals to a lot of queries and I want to avoid that.
My question is - is it possible to, for example, prefetch all the values for each site (SiteFieldValue.objects.filter(site=record).first() somewhere in the SiteListTable class), put them into an array and then use them in the def render_ functions by simply checking the value assigned to a key (id of the field).
Models:
class Site(models.Model):
name = models.CharField(max_length=100)
class SiteField(models.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length=500, null=True, blank=True)
def __str__(self):
return self.name
class SiteFieldValue(models.Model):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
field = models.ForeignKey(SiteField, on_delete=models.CASCADE)
value = models.CharField(max_length=500)
Table view
class SiteListTable(tables.Table):
name = tables.Column()
importance = tables.Column(verbose_name='Importance',empty_values=())
vertical = tables.Column(verbose_name='Vertical',empty_values=())
#... and many more to come... all values based on siteFieldValue
def render_importance(self, value, record):
q = SiteFieldValue.objects.filter(site=record, field=1).first()
# ^^ I don't want this!! I would want the SiteFieldValue to be prefetched somewhere else for that model and just check the array for field id in here.
if (q):
return q.value
else:
return None
def render_vertical(self, value, record):
q = SiteFieldValue.objects.filter(site=record, field=2).first()
# ^^ I don't want this!! I would want the SiteFieldValue to be prefetched somewhere else for that model and just check the array for field id in here.
if (q):
return q.value
else:
return None
class Meta:
model = Site
attrs = {
"class": "table table-striped","thead" : {'class': 'thead-light',}}
template_name = "django_tables2/bootstrap.html"
fields = ("name", "importance", "vertical",)
This might get you started. I've broken it up into steps but they can be chained quite easily.
#Get all the objects you'll need. You can filter as appropriate, say by site__name).
qs = SiteFieldValue.objects.select_related('site', 'field')
#lets keep things simple and only get the values we want
qs_values = qs.values('site__name','field__name','value')
#qs_values is a queryset. For ease of manipulation, let's make it a list
qs_list = list(qs_values)
#set up a final dict
final_dict = {}
# create the keys (sites) and values so they are all grouped
for site in qs_list:
#create the sub_dic for the fields if not already created
if site['site__name'] not in final_dict:
final_dict[site['site__name']] = {}
final_dict[site['site__name']][site['name']] = site['site__name']
final_dict[site['site__name']][site['field__name']] = site['value']
#now lets convert our dict of dicts into a list of dicts
# for use as per table2 docs
data = []
for site in final_dict:
data.append(final_dict[site])
Now you have a list of dicts eg,
[{'name':site__name, 'col1name':value...] and can add it as shown in the table2 docs
from django.db import models
class Game(models.Model):
description = models.TextField(max_length=8192)
class GamePreview(models.Model):
game = models.OneToOneField(Game, on_delete=models.CASCADE)
comments = models.IntegerField(default=0) # Want to + 1 this when a comment gets created
class GameComment(models.Model):
game = models.ForeignKey(Game, on_delete=models.CASCADE)
comment = models.CharField(max_length=512)
#classmethod # does not work
def create(cls, game):
comment = cls(game=game)
preview = GamePreview.objects.get(game=comment.game)
preview.comments += 1
return preview
Basically, I have a GamePreview model that has a IntgerField that should show the amount of comments, but I cannot figure out how I can do preview.comments += 1 when a GameComment gets created...
Please don't, you can annotate the GamePreview object to determin the number of comments.
You thus can remove the comments field:
class GamePreview(models.Model):
game = models.OneToOneField(Game, on_delete=models.CASCADE)
# no comments
and then in case you need the number of related GameComments, you can work with .annotate(…) [Django-doc]:
from django.db.models import Count
GamePreview.objects.annotate(
comments=Count('game__gamecomment')
)
GamePreview objects that arise from this queryset will have an extra attribute .comments that contains the number of related GameComments.
If you really want to increment the number of comments, you can work with:
class GameComment(models.Model):
game = models.ForeignKey(Game, on_delete=models.CASCADE)
comment = models.CharField(max_length=512)
#classmethod # does not work
def create(cls, game, comment):
comment = cls(game=game, comment=comment)
preview = GamePreview.objects.get(game_id=comment.game_id)
preview.comments = F('comments') + 1
preview.save()
return comment
But this is usually not a good idea: if a comment is removed, or no longer belongs to that game but to another, you will need to write some logic to change this. Often it is quite hard to cover all possible cases.
I have a QuestionSet that is a manytomany of questions. Each QuestionSet has multiple questions associated with it.
I want to get a list of all unique questions that are in two (or more) QuestionSets
For example:
First QuestionSet = Questions(1,2,3)
Second QuestionSet = Questions(1,3,4)
Output:
List/Queryset/Something of Questions(1,2,3,4)
models.py
class QuestionSet(models.Model):
name = models.CharField(max_length=255)
questions = models.ManyToManyField(Question)
class Question(models.Model):
question_num = models.IntegerField(default=0,blank=False)
Maybe try this:
questions = Question.objects.annotate(question_set_count=Count('questionset')).filter(question_set_count__gte=2)
You can find that more in the docs: Django aggregates
first of all, I'm aware that this question might've been answered already, but there are two reasons why I'm opening another question: One, obviously, is I'm struggling with the Django syntax. Secondly, and perhaps more importantly, I'm not quite sure whether my database setup makes even sense at this point. So, please bear with me.
I work in a hospital and one of my daily stuggles is that, oftentimes, one single drug can have a lot of different names. So, I thought that'd be a good task to practice some Django with.
Basically I want two databases: One that simply links the drugs "nick name" to it's actual name. And another one which links the actual name to some additional information, something along the lines of a wiki page.
What I've come up with so far:
(django)django#ip:~/medwiki$ cat medsearch/models.py
from django.db import models
# Create your models here.
class medsearch(models.Model):
proprietary_name = models.CharField(max_length = 100, unique = True)
non_proprietary_name = models.CharField(max_length = 100, unique = True)
def __str__(self):
return self.non_proprietary_name
class medwiki(models.Model):
proprietary_name = models.ForeignKey('medisearch', on_delete=models.CASCADE)
cetegory = models.CharField(max_length = 255)
#wiki = models.TextField() etc.
def __str__(self):
return self.proprietary_name
(django)django#ip-:~/medwiki$
So, I can add a new "medsearch object" just fine. However, when adding the "Category" at medwiki I get __str__ returned non-string (type medsearch). Presumably, because there's more than one key in medsearch? I thus suspect that "FroeignKey" is not suited for this application and I know that there are other ways to link databases in Django. However, I don't know which one to choose and how to implement it correctly.
Hopefully, some of you have some ideas?
EDIT: Here's what I've come up with so far:
class Proprietary_name(models.Model):
proprietary_name = models.CharField(max_length = 100, unique = True)
def __str__(self):
return self.proprietary_name
class Category(models.Model):
category = models.CharField(max_length = 100, unique = True)
def __str__(self):
return self.category
class Mediwiki(models.Model):
proprietary_name = models.ManyToManyField(Proprietary_name)
non_proprietary_name = models.CharField(max_length = 100, unique = True)
category = models.ManyToManyField(Category)
wiki_page = models.TextField()
def __str__(self):
return self.non_proprietary_name
Now I can attribute different categorys and different proprietary_names to one drug. Which works great so far.
So does looking up the non-proprietary_name when I know the proprietary "nick name".
>>> Mediwiki.objects.get(proprietary_name__proprietary_name="Aspirin")
<Mediwiki: acetylsalicylic acid>
>>>
However, I'd also like to display all the proprietary_names, when I know the non_proprietary_name. Do I have to further change the database design, or am I just missing some other thing here?
This would work:
return self.proprietary_name.proprietary_name
But that doesn't really make sense !
The main issue is that you've called the foreign key to medsearch, proprietary_name.
The second issue is just a convention one. In Python (and many programming languages), classes must start with an uppercase letter.
The following would be better:
class MedSearch(models.Model):
proprietary_name = models.CharField(max_length=100, unique=True)
non_proprietary_name = models.CharField(max_length=100, unique=True)
def __str__(self):
return self.non_proprietary_name
class MedWiki(models.Model):
med_search = models.ForeignKey('MedSearch', on_delete=models.CASCADE, related_name='wikis')
cetegory = models.CharField(max_length = 255)
#wiki = models.TextField() etc.
def __str__(self):
return self.med_serach.proprietary_name
As you note, the proprietary_name field on medwiki is a ForeignKey. You can't return that value directly from the __str__ method because that needs to return a string. You need to convert that value into a string before returning it: either use the default string representation of the medsearch instance:
return str(self.proprietary_name)
or choose a specific string field to return:
return self.proprietary_name.proprietary_name
I have an object, that is uploaded by the user, it contains several details, but for the sake of clarity, can be simply defined by the following model representation -
After this other users can upvote and downvote what this user has uploaded hence a vote model.
Now I want to get the upvotes and downvotes of all the objects to be displayed in the template. Hence I add two functions to the ObjectDetail class, as upvote and downvote.
The trouble with this model is, say there are 20 objects, for each object 2 queries are fired one to get the upvote and the other to get the downvote. Hence the no. of queries are 40 now for 20 objects.
What would be a good way to tweak this to reduce the number of queries, and display the upvotes and downvotes on each of the object?
class ObjectDetail(models.Model):
title = models.CharField()
img = models.ImageField()
description = models.TextField()
uploaded_by = models.ForeignKey(User, related_name='voted_by')
#property
def upvote(self):
upvote = Vote.objects.filter(shared_object__id = self.id,
vote_type = True).count()
return upvote
#property
def downvote(self):
downvote = Vote.objects.filter(shared_object__id = self.id,
vote_type = False).count()
return downvote
class Vote(models.Model):
vote_type = models.BooleanField(default = False)
voted_by = models.ForeignKey(User, related_name='voted_by')
voted_for = models.ForeignKey(User, related_name='voted_for')
shared_object = models.ForeignKey(ObjectDetail, null=True, blank=True)
dtobject = models.DateTimeField(auto_now_add=True)
On one hand, django does give you the capability to write raw SQL when you have to. But this example is simple, you should not have to use raw SQL to get this information.
Django will put off making a query until you access the results of a queryset. So you can try to compose the whole query using querysets and Q objects and then access the results on the composed query - this should trigger one DB query (or one per model, rather than one per instance) for all the results.
So, how to do that? You want to get all the Vote records for a given set of ObjectDetail records. I'm going to assume you have a list of ids of ObjectDetail records.
Unfortunately, your upvote and downvote properties return the result of "count" on their query sets. This counts as an "access to the results" of the queryset produced by the "filter" call. I would change those method definitions to refer to the backwards-relation object manager vote_set, like so:
#property
def upvote(self):
answer = 0
for vote in self.vote_set.all ():
if vote.vote_type:
answer += 1
return answer
#property
def downvote(self):
answer = 0
for vote in self.vote_set.all ():
if not vote.vote_type:
answer += 1
return answer
Note we just access the query set of votes for the current object. At this stage, we are assuming that the orm can access the cached results.
Now, in the view and/or template, we want to assemble the big complex query.
My example is a functional view:
def home (request):
# just assigning a constant list for simplicity.
# Also was lazy and did 10 examples rather than 20.
objids = [ 1, 5, 15, 23, 48, 52, 55, 58, 59, 60 ]
# make a bunch of Q objects, one for each object id:
q_objs = []
for objid in objids:
q_objs.append(Q(id__exact = objid))
# 'or' them together into one big Q object.
# There's probably a much nicer way to do this.
big_q = q_objs[0]
for q_obj in q_objs[1:]:
big_q |= q_obj
# Make another queryset that will ask for the Vote objects
# along with the ObjectDetail objects.
# Try commenting out this line and uncommenting the one below.
the_objects = ObjectDetail.objects.filter(big_q).prefetch_related('vote_set')
# the_objects = ObjectDetail.objects.filter(big_q)
template = 'home.html'
context = {
'the_objects' : the_objects,
}
context_instance = RequestContext (request)
return render_to_response (template, context, context_instance)
Here are some pointers to related documentation:
https://docs.djangoproject.com/en/1.5/topics/db/queries/#querysets-are-lazy
https://docs.djangoproject.com/en/1.5/ref/models/querysets/#when-querysets-are-evaluated
https://docs.djangoproject.com/en/1.5/topics/db/queries/#complex-lookups-with-q-objects
https://docs.djangoproject.com/en/1.5/topics/db/queries/#following-relationships-backward
It's in the docs at https://docs.djangoproject.com/en/1.5/ref/models/querysets/#django.db.models.query.QuerySet.extra
I am using the extra() clause to inject some raw sql here.
EDIT: this works with an app called 'vot' and at least Sqlite. Change the vot_* table names to your needs.
from django.db.models import Count
objects = ObjectDetail.objects.all().extra(
select={ 'upvotes': '''SELECT COUNT(*) FROM vot_vote
WHERE vot_vote.shared_object_id = vot_objectdetail.id
AND vot_vote.vote_type = 1''',
'downvotes': '''SELECT COUNT(*) FROM vot_vote
WHERE vot_vote.shared_object_id=vot_objectdetail.id
AND vot_vote.vote_type = 0'''})
Now each element in objects has a upvotes and downvotes property.