Flask SQLAlchemy Foreign Key Querying - flask

I am quite new to Python and programming in general. I could use some help with logic.
I have 3 tables, users, posts, comments. Table comments has a column "user_id" which has a Foreign_Key to the users column "id". Also as a post_id with FK to post_id.
comments.user_id = users.id && comments_post_id = posts.id
To query a specific post I am using:
post = Posts.query.filter_by(id=post_id).first_or_404()
To query the comments I am using:
comments = Comments.query.filter_by(post_id=post_id).order_by(Comments.time.desc()).all()
I would like to get the username from the users table where the comments.user_id.... This is where my logic gets scrambled.
Using the FK can I directly reference Users.username? Or does the FK just map a relationship so I can run the query. Where comments.user_id = Users.id .... get username and everything. I feel I am doing this quite inefficiently.
I'd like to replace comment.user_id with "Users.username where comment.user_id = user_id"
{% if comments %}
{% for comment in comments %}
{{ comment.user_id }}
{{ comment.time }}
{{ comment.comment }}
{% endfor %}
{% endif %}
I think what I need to do I make a new column in comments, comments.user_username & give it an FK of users.username. I keep getting a 150 error and the columns are identical. Same collation, datatype and length.

You can define a attribute which type is relationship of SQLAlchemy as follows:
class Comment(db.Model):
id = db.Column(db.Integer(), primary_key=1)
content = db.Column(db.Text(), nullable=0)
create_user_id = db.Column(db.Integer(), db.ForeignKey("user.id"))
create_user = db.relationship("User", foreign_keys=create_user_id)
post_id = dn.Column(db.Integer(), db.ForeignKey("post.id"))
You can write comment.create_user.username in the template file.

Related

Prefetching model with GenericForeignKey

I have a data structure in which a Document has many Blocks which have exactly one Paragraph or Header. A simplified implementation:
class Document(models.Model):
title = models.CharField()
class Block(models.Model):
document = models.ForeignKey(to=Document)
content_block_type = models.ForeignKey(to=ContentType)
content_block_id = models.CharField()
content_block = GenericForeignKey(
ct_field="content_block_type",
fk_field="content_block_id",
)
class Paragraph(models.Model):
text = models.TextField()
class Header(models.Model):
text = models.TextField()
level = models.SmallPositiveIntegerField()
(Note that there is an actual need for having Paragraph and Header in separate models unlike in the implementation above.)
I use jinja2 to template a Latex file for the document. Templating is slow though as jinja performs a new database query for every Block and Paragraph or Header.
template = get_template(template_name="latex_templates/document.tex", using="tex")
return template.render(context={'script': self.script})
\documentclass[a4paper,10pt]{report}
\begin{document}
{% for block in chapter.block_set.all() %}
{% if block.content_block_type.name == 'header' %}
\section{ {{- block.content_block.latex_text -}} }
{% elif block.content_block_type.name == 'paragraph' %}
{{ block.content_block.latex_text }}
{% endif %}
{% endfor %}
\end{document}
(content_block.latex_text() is a function that converts a HTML string to a Latex string)
Hence I would like to prefetch script.blocks and blocks.content_block. I understand that there are two methods for prefetching in Django:
select_related() performs a JOIN query but only works on ForeignKeys. It would work for script.blocks but not for blocks.content_block.
prefetch_related() works with GenericForeignKeys as well, but if I understand the docs correctly, it can only fetch one ContentType at a time while I have two.
Is there any way to perform the necessary prefetching here? Thank you for your help.
Not really an elegant solution but you can try using reverse generic relations:
from django.contrib.contenttypes.fields import GenericRelation
class Paragraph(models.Model):
text = models.TextField()
blocks = GenericRelation(Block, related_query_name='paragraph')
class Header(models.Model):
text = models.TextField()
level = models.SmallPositiveIntegerField()
blocks = GenericRelation(Block, related_query_name='header')
and prefetch on that:
Document.objects.prefetch_related('block_set__header', 'block_set__paragraph')
then change the template rendering to something like (not tested, will try to test later):
\documentclass[a4paper,10pt]{report}
\begin{document}
{% for block in chapter.block_set.all %}
{% if block.header %}
\section{ {{- block.header.0.latex_text -}} }
{% elif block.paragraph %}
{{ block.paragraph.0.latex_text }}
{% endif %}
{% endfor %}
\end{document}
My bad, I did not notice that document is an FK, and reverse FK can not be joined with select_related.
First of all, I would suggest to add related_name="blocks" anyway.
When you prefetch, you can pass the queryset. But you should not pass filters by doc_id, Django's ORM adds it automatically.
And if you pass the queryset, you can also add select/prefetch related call there.
blocks_qs = Block.objects.all().prefetch_related('content_block')
doc_prefetched = Document.objects.prefetch_related(
Prefetch('blocks', queryset=blocks_qs)
).get(uuid=doc_uuid)
But if you don't need extra filters or annotation, the simpler syntax would probably work for you
document = (
Document.objects
.prefecth_related('blocks', 'blocks__content_block')
.get(uuid=doc_uuid)
)

Django Haystack: responsibilities of the template and model indexes

I've gone over the docs, and I've even created a few search back ends, but I"m still really confused on what these things do in haystack. Is the search back end searching the fields you put in your class that inherits indexes.SearchIndex, indexes.Indexable, or is the back end searching the text inside your template? Can someone explain this to me?
In django haystack you will create a class that defines what fields should be queried (well that's how I understand it) like so:
class ProductIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
name = indexes.CharField(model_attr='title', boost=1.75)
description = indexes.CharField(model_attr='description')
short_description = indexes.CharField(model_attr='short_description')
def get_model(self):
return Product
def index_queryset(self, using=None):
"""Used when the entire index for model is updated."""
return self.get_model().objects.filter(active=True,
published_at__lte=datetime.now())
You'll also create a template txt that will do something - I"m not sure what. I know that the search backend will go over this template during the searching algorithm.
{{ object.name }}
{{ object.description }}
{{ object.short_description }}
{% for related in object.related %}
{{ related.name }}
{{ related.description }}
{% endfor %}
{% for category in object.categories.all %}
{% if category.active %}
{{ category.name }}
{% endif %}
{% endfor %}
As you can see the template has some fields that my index class doesn't have, however, these will be searched by the search backend. So why even have fields in the index? What are the rolls of the index class, and the index template? Can someone please explain this to me.
The ProductIndex class is the main thing here. Haystack will use this configuration to index your Product model according to the fields you have chosen to be indexed and in what way. You can read more about it here.
The template which you have created will be used by this field text = indexes.CharField(document=True, use_template=True). In this template we include every important data from model or related models, why? because this is used to perform search query on all data if you don't want to lookup in just one field.
# filtering on single field
qs = SearchQuerySet().models(Product).filter(name=query)
# filtering on multiple fields
qs = SearchQuerySet().models(Product).filter(name=query).filter(description=query)
# filtering on all data where ever there is a match
qs = SearchQuerySet().models(Product).filter(text=query)

Django: retrieving ManyToManyField objects with minimum set of queries

My code looks like this:
models.py
class Tag(models.Model):
name = models.CharField(max_length=42)
class Post(models.Model):
user = models.ForeignKey(User, related_name='post')
#...various fields...
tags = models.ManyToManyField(Tag, null=True)
views.py
posts = Post.objects.all().values('id', 'user', 'title')
tags_dict = {}
for post in posts: # Iteration? Why?
p = Post.objects.get(pk=[post['id']]) # one extra query? Why?
tags_dict[post['id']] = p.tags.all()
How am I supposed to create a dictionary with tags for each Post object with minimum set of queries? Is it possible to avoid iterating, too?
Yes you will need a loop. But you can save one extra query in each iteration, you don't need to get post object to get all its tags. You can directly query on Tag model to get tags related to post id:
for post in posts:
tags_dict[post['id']] = Tag.objects.filter(post__id=post['id'])
Or use Dict Comprehension for efficiency:
tags_dict = {post['id']: Tag.objects.filter(post__id=post['id']) for post in posts}
If you have Django version >= 1.4 and don't really need a dictionary, but need to cut down the count of queries, you can use this method like this:
posts = Post.objects.all().only('id', 'user', 'title').prefetch_related('tags')
It seems to execute only 2 queries (one for Post and another for Tag with INNER JOIN).
And then you can access post.tags.all without extra queries, because tags was already prefetched.
{% for post in posts %}
{% for tag in post.tags.all %}
{{ tag.name }}
{% endfor %}
{% endfor %}

How can I filter data in this way?

models.py:
class InjuredLocation(models.Model):
reportperson = models.ForeignKey(ReportPerson)
mark1 = models.BooleanField('Mark1', default=False)
mark2 = models.BooleanField('Mark2', default=False)
mark3 = models.BooleanField('Mark3', default=False)
class Report(models.Model):
user = models.ForeignKey(User, null=False)
report_number = models.CharField('report Number', max_length=100)
class ReportPerson(models.Model):
report = models.ForeignKey(Report)
action_type = models.CharField(max_length=100, choices=ACTION_TYPE)
name = models.CharField('Name', max_length=100)
This is my three models, I want to filter the data from InjuredLocation models.
Reportperson table contain report id and name field of that table could be multiple.Each report can have multiple names.
I want to filter the data from InjuredLocation table with reference to reportperson_id.
The filtered data should be for the equivalent report.
tried:
injury_list = []
reportperson = ReportPerson.objects.filter(report=report_id, action_type="involved")
injuary_mark = InjuredLocation.objects.filter(pk=reportperson)
for injuary in injuary_mark:
mark = InjuredLocation.objects.get(pk=injuary.id)
marklist={'mark':mark}
injury_list.append(marklist)
I am getting this error "(1242, 'Subquery returns more than 1 row')" in 5th line,if Reportperson table have more than one name.
update:
injuery_list = []
injuries = InjuredLocation.objects.filter(reportperson__report=report_id, reportperson__action_type="involved")
for reportperson in injuries:
injun = InjuredLocation.objects.get(pk=reportperson.id)
list_inju = {'person': injun}
injuery_list.append(list_inju)
Able to take the objects from InjuredLocation models,in template i rendered it but problem is "it should render against reportperson_id,instead it is rendering all" for example if InjuredLocation models have reportperson_id=1,mark1=0 & mark2=1 and for reportperson_id=2,mark1=1 & mark2=0 it is rendering all like this "1 1" for both reportperson_id.The expected output is 0 1 and 1 0.What ever selected are all comes to display for all reportperson_id.
template is
{%for injuary_mark in injuery_list%}
{%if injuary_mark.person.mark1 %}<img style="float: right; margin:5px 4px -35px 0;" src="{{BASE_URL}}/static/images/red-cross.png"> {% endif %}
{%if injuary_mark.person.mark2 %}<img style="float: right;margin:5px 8px -35px -8px;" src="{{BASE_URL}}/static/images/red-cross.png"> {% endif %}
{%endfor%}
Last update:
I want to show the details in InjuredLocation models against id in the Reportperson models.This is from a single report,see the Report models in the figure.
All the three models with data i pasted below.
What i required as output is,a tab will be dynamically created when a row in InjuredLocation model is created against id in Reportperson table.I want to show the mark from InjuredLocation table against their respective id from Reportperson table in their respective tabs.Now all the mark whatever created against id in Reportperson model are shown in all tabs.Assume mark for id=1 is empty and Marks for id=2 and id=3 are their in database,as per requirement tab1 should not show any data,but now tab1 is showing data of tab2 and tab3 from id=2 and id=3's data.Need help
Your problem is with this line:
injuary_mark = InjuredLocation.objects.filter(pk=reportperson)
The exception refers to a different line because that's where the queryset is actually evaluated.
There are two problems here.
The fatal one is that reportperson is not a single value - it's a queryset:
reportperson = ReportPerson.objects.filter(report=report_id, action_type="involved")
As you note, 'each report can have multiple names' - and this will find all of them that match the action_type, so it's not an appropriate value to use in an exact lookup.
Also, you almost certainly do not mean pk=reportperson - even if reportperson were a single value, you're filtering on the wrong field.
The fix is somewhat dependent on what exactly you want to do with the multiple names. If you just want to get all the InjuredLocation instances that relate to the report_id regardless of report name, this is a more concise expression:
injuries = InjuredLocation.objects.filter(reportperson__report_id=report_id, reportperson__action_type="involved")
If necessary you could use your original reportperson lookup and then an __in filter, but the version above using __ to filter on related values is more concise. In the database, __in uses a subquery while filtering using __ performs a join; the two can have different performance. The __in version would be:
reportpeople = ReportPerson.objects.filter(report=report_id, action_type="involved")
injuries = InjuredLocation.objects.filter(reportperson__in=reportpeople)
If you want to keep each InjuredLocation instance with its ReportPerson instance, say because you're going to group them in a template:
reportpeople = ReportPerson.objects.filter(report_id=report_id, action_type="involved")
for reportperson in reportpeople:
injuries = reportperson.injuredlocation_set.all()
# now do something with them
Edited:
if you given me a sample how to make the queryset and how to iterate in template will be a great help for me
Something like:
In the view:
reportpeople = ReportPerson.objects.filter(report_id=report_id, action_type="involved")
return render('mytemplate.html', {'reportpeople': reportpeople})
In the template:
{% for reportperson in reportpeople %}
<p>Report name: {{ reportperson.name }}</p>
<ul>
{% for injured_location in reportperson.injuredlocation_set.all %}
<li>
{% if injured_location.mark1 %}
<img style="float: right; margin:5px 4px -35px 0;" src="{{BASE_URL}}/static/images/red-cross.png"> {% endif %}
{% if injured_location.mark2 %}
<img style="float: right;margin:5px 8px -35px -8px;" src="{{BASE_URL}}/static/images/red-cross.png"> {% endif %}
</li>
{% endfor %}
</ul>
{% endfor %}
Or whatever HTML you want for each report name. The point is that you can get at the InjuredLocation instances related to a particular ReportPerson instance via the injuredlocation_set manager.

Django: dynamically access queryset data from different models

I'm interested in learning how to display all attributes of a list of querysets that come from different models.
Here's an example:
models.py
class MyModelA(models.Model):
attr1 = something
attr2 = something
class MyModelB(models.Model):
attr3 = something
attr4 = something
class MyModelC(models.Model):
attr5 = something
attr6 = something
views.py
Let's say we have three model instances that are stored in a list:
all_selected_queries = [mymodela, mymodelb, mymodelc]
For each queryset in the list, I want to display all model field titles and data in a template.
My approach:
# Loop through the list and get the verbose name title of each field ("titel")
for z in all_selected_queries:
queryset_fields = z._meta.get_fields()
for f in queryset_fields:
titel = f.verbose_name.title()
return titel
What challenges me is how to get the fields' values without having to include the actual attribute name (because they are different for each queryset).
So instead of explictly calling
f.attr1, f.attr2, f.attr3, f.attr4, f.attr5
for each field, I'd like to encounter a solution that works across model boundaries.
Thank you very much for your help!
You can try like this:
v_list = list()
for z in all_selected_queries:
queryset_fields = z._meta.get_fields()
values = dict()
for f in queryset_fields:
values[f.verbose_name.title()] = getattr(z, f.attname)
v_list.append(values)
return render(request,'some_template.html',{'values':v_list})
And show them in template:
{% for value in values %}
{% for key, val in value.items %}
<b>{{ key }}: </b>{{ val }}
{% endfor %}
{% endfor %}