SQL Alchemy: Relationship with grandson - python

I'm building a SQL Alchemy structure with three different levels of objects; for example, consider a simple database to store information about some blogs: there are some Blog object, some Post object and some Comment objects. Each Post belongs to a Blog and each Comment belongs to a Post. Using backref I can automatically have the list of all Posts belonging to a Blog and similarly for Comments.
I drafted a skeleton for such a structure.
What I would like to do now is to have directly in Blog an array of all the Comments belonging to that Blog. I've tried a few approaches, but they don't work or even make SQL Alchemy cry in ways I can't fix. I'd think that mine is quite a frequent need, but I couldn't find anything helpful.
Colud someone suggest me how to do that?
Thanks.

I think you can use an association proxy
class Blog(Base):
comments = association_proxy('posts', 'comments')

You need to use join:
comments = session.query(Comment).join(Post).filter(Post.blog == b1).all()

Related

Benefits of foreign key over integerfield

I'm not sure if this is an appropriate question here. I know the answer but I don't really know why, and I need proof when I raise this to my team.
We have a number of Blog Posts on a Django Site. It's possible to "clone" one of those blog posts to copy it to another site. The way the current developer did that was to take the pk of the original post and store it as an IntegerField on the cloned post as clone_source. Therefore to get a story's clones, we do:
clones = BlogPost.all_sites.filter(clone_source=pk)
It seems to me that this would be much better structured as a foreign key relationship.
Am I right? Why or why not?
Deleted objects
If you ever decided to delete the original post, you'd need a separate query to handle whatever you expect to do with the cloned posts instead of using the on_delete kwarg of a FK.
Its an extra query
As noted in the comments, foreign keys allow you to traverse the relationships directly through the ORM relationship methods.
Data structure visualisation tools
These won't be able to traverse any further down from an integer field since it will believe it is at a leaf node.
Throughout all of this though, the elephant in the room is that a "clone" is still just duplicated data so I wonder why you don't just let a blog post be referenced more than once then you don't need to worry about how you store clones.

Django: queryset multiple conditions or gather into new object

I need to get all Comments for all Projects that includes a specific User. Meaning, all comments for all projects that a user is member of.
A user can belong to many Projects, and each Project has many Comments.
How should this be done right? So far I have solved it in my template by creating a nested for-loop, but that's not good since I need to sort the result.
I'm thinking something like:
projects = user.projects
comments = Comment
for p in projects:
for c in p.comments:
comments.append(c)
return comments
...does not seem to work.
Any clues?
I think this will do it:
query = Comment.objects.filter(project__user=person)
If the Comment model has a foreign key to project which has a foreign key to user. This will involve a SQL join statement in the database. It's better to do this on the the database because it's far more efficient. Databases are designed exactly to do this.

Django - sort QuerySet by backwards related model's field

I have the following (simplified) models:
class Post(models.Model):
title=models.CharField(max_length=500)
class Recommendation(models.Model):
post=models.ForeignKey(Post)
submit_time=models.DateTimeField(auto_now_add=True)
And I want to get the list of distinct Posts ordered by Recommendation's submit time.
The first way I tried was the straighforward:
Post.objects.order_by('recommendation__submit_time').distinct()
But surprisingly this gave a QuerySet with duplicate Post objects. Turns out the rows are actually different because Django adds extra columns for the ordering, but does not return them in the results.
Looking around I found a couple answers on SO, including to use aggregation instead of ordering:
Post.objects.all().annotate(Max('recommendation__submit_time')).order_by('recommendation__submit_time__max')
Or to de-normalize the model and add a last_recommended_time field to Post.
Most of the questions/answers already in SO are a couple years old, so I was wondering if there's a more idiomatic and straightforward way to do this than those suggested hacks.
EDIT:
Just thought I made it clear:
The solutions listed above do work and I'm using them (albeit not in production). I'm just interested in better solutions to this issue.
Have you thought about using raw sql
Post.objects.raw("""
SELECT DISTINCT post FROM
(SELECT appname_post.post_id AS post, appname_recommendation.submit_time
FROM appname_post
INNER JOIN appname_recommendation
ON appname_post.post_id = appname_recommendation.post_id
ORDER_BY appname_recommendation.submit_time)
""")

Custom sql for Django model

A bit of background...
I'm trying to create a custom auth backend and extend the user model. I'm using the following as a blue print:
blog post by Scott Barnham
For whatever reason, the ORM is generating invalid sql. It seems to want to do a inner join back to itself and it's failing because it can't find a field named user_ptr_id for the join.
If you do a search for this, it seems that I might not be the only one. And there is actually a reference to this in a comment on the blog post above. But, I can't seem to fix it.
It seems like I should be able to override the SQL that is getting generated. Is that correct? From what I can tell, it seem like I might do this with a custom Object manager. Correct?
However, I can't seem to find a good example of what I want to do. Everything that I see is wanting to inherit and chain them. That's not really what I want to do. I sort of just want to say something like:
hey Django! on a select, use this SQL statement. etc
Is this possible? Maybe my "googlin'" is off today, but I can't seem to find it. That leads me to believe I'm using wrong terms or something.
Please note: I'm using Django 1.3.1 with Python 2.6.5 and PostgreSQL 9.1
David,
Yes, you can override the behavior of a model by implementing an overriding Manager in the object. I found a great blog by Greg Allard on A Django Model Manager for Soft Deleting Records which runs through a soft delete, to set a field deleted to True/False and only show objects that are not deleted, or all with deleted objects.
With that in mind, I would think you could override your object's all(), or filter() methods to get what you want. As an aside, everytime I have used a pointer, "ptr" is evident in the name of the field, it is because of class inheritance. For example, class Animal():..., class Man(Animal): Man extends or is a subclass of Animal. In the database, the Man table will have an animal_ptr_id which "extends" the animal table's tuple with that id as a Man with ANIMAL fields and MAN fields JOINed.

Filtering by relation count in SQLAlchemy

I'm using the SQLAlchemy Python ORM in a Pylons project. I have a class "Project" which has a one to many relationship with another class "Entry". I want to do a query in SQLAlchemy that gives me all of the projects which have one or more entries associated with them. At the moment I'm doing:
[project for project in Session.query(Project) if len(project.entries)>0]
which I know isn't ideal, but I can't figure out how to do a filter that does what I require (e.g. Session.query(Project).filter(Project.entries.exists())).
Any ideas?
Session.query(Project).filter(Project.entries.any()) should work.
Edited credit of James Brady's comment, be sure to give him some love.

Categories