I trying to select some data using Peewee ORM,but I'm confused how to use foreign key correctly.
I wanna select post_title,user_name,act_title by Act.id(default primary key in act).
So I use this
Post.select(Post.post_tile,User.user_name,Act.act_title).join(Act).join(User).where(Act.id==actId)
But I got this:
[{"post_title": null,"user": {}, "act": {}}]
Here is my model:
class User(BaseModel):
user_name = CharField(max_length=30,unique=True)
user_email = CharField(max_length=60,unique=True)
class Act(BaseModel):
user = ForeignKeyField(User, related_name='users_act_id') #foreignkey
act_title = CharField(max_length=30)
class Post(BaseModel):
act = ForeignKeyField(Act,related_name='acts_id') #foreignkey
user = ForeignKeyField(User,related_name='users_post_id') #foreignkey
post_title = CharField(max_length=30)
If you want to join both the User and the Act table on the Post table, you need to put a switch in the query, so you would have
Post.select(Post.post_tile,User.user_name,Act.act_title).join(Act).switch(Post).join(User).where(Act.id==actId)
Although it's possible that's not the result you're looking for because you have user as a foreign key in both the Act and Post models
I think the only thing you're missing is not looking up the values on the joined instances:
posts = (Post
.select(Post.post_tile,User.user_name,Act.act_title)
.join(Act)
.switch(Post)
.join(User)
.where(Act.id==actId))
for post in posts:
print post.post_title, post.user.user_name, post.act.act_title
If you want the attributes all assigned to the post object, just tack on a call to .naive(), e.g.:
posts = (Post.select()...where(Act.id==actId).naive())
Related
Let's say I have a User model with attributes id, name, email and a relationship languages.
Is it possible to create a User instance from existing data that behaves like I would have queried it with dbsession.query(User).get(42)?
What I mean in particular is that I want that an access to user.languages creates a subquery and populates the attribute.
Here a code example:
I have a class User:
class User(Base):
id = Column(Integer, primary_key=True)
name = Column(String(64))
email = Column(String(64))
languages = relationship('Language', secondary='user_languages')
I already have a lot of users stored in my DB.
And I know that I have, for example, this user in my DB:
user_dict = {
'id': 23,
'name': 'foo',
'email': 'foo#bar',
}
So I have all the attributes but the relations.
Now I want to make a sqlalchemy User instance
and kind of register it in sqlalchemy's system
so I can get the languages if needed.
user = User(**user_dict)
# Now I can access the id, name email attributes
assert user.id == 23
# but since sqlalchemy thinks it's a new model it doesn't
# lazy load any relationships
assert len(user.languages) == 0
# I want here that the languages for the user with id 23 appear
# So I want that `user` is the same as when I would have done
user_from_db = DBSession.query(User).get(23)
assert user == user_from_db
The use-case is that I have a big model with lots of complex
relationships but 90% of the time I don't need the data from those.
So I only want to cache the direct attributes plus what else I need
and then load those from the cache like above and be able to
use the sqlalchemy model like I would have queried it from the db.
From the sqlalchemy mailing list:
# to make it look like it was freshly loaded from the db
from sqlalchemy.orm.session import make_transient_to_detached
make_transient_to_detached(user)
# merge instance in session without emitting sql
user = DBSession.merge(user, load=False)
This answer was extracted from the question
I have 3 models: User, Choice, Card. Each user will look at the same set of 10 cards and decides each one is important or not.
Here are how I define the classes and their relationship
In models.py:
class Choice(models.Model):
user = models.ForeignKey(User)
card = models.ManyToManyField(Card)
is_important = models.NullBooleanField()
class Card(models.Model):
card_number = models.IntegerField(primary_key=True)
content = models.TextField(null=False)
In views.py
(I try to save the choice for the card from the user. )
def listings(request):
user = request.user
choice = Choice.objects.create(user=user, is_important = True)
choice.card= Card.objects.get(1)
However, I got this error
'Card' object is not iterable
Could you please show me where the error is?
Many thanks!
You can add object against many to many field like this
card = Card.objects.create(card_number=any_number, content='abc')
choice.card.add(card)
First, it looks like you forgot pk= in your first .get() argument: Card.objects.get(pk=1)
Second, Choice.cards is a ManyToManyField that expects a list of items and not one in particular. You should set it through:
choice.card.set(Card.objects.filter(pk=1))
Please note that direct assignment with = will be deprecated from Django 1.10 and deleted in Django 2.0
.filter() will return a QuerySet (which is iterable). I think you wanted a ForeignKey instead of a M2M field, in which case your code would work (with the additional pk=).
In your function:
def listings(request):
user = request.user
choice = Choice.objects.create(user=user, is_important = True)
choice.card= Card.objects.get(1)
The following line is trying to fetch the Card object. However, we need to specify which card to be fetched.
If using an id, query it as:
choice.card= Card.objects.get(pk=1)
or else using list of ids:
choice.card = Card.objects.filter(pk__in=[12,22])
If using card_number field:
choice.card= Card.objects.get(card_number=1)
or else using list of card_numbers:
choice.card = Card.objects.filter(card_number__in=[12,22])
I want to create a form for many-to-many relations using Flask, SQLAlchemy and WTForms that represents these models:
personaddress = db.Table('personaddress',
db.Column('person', db.Integer, db.ForeignKey('person.id')),
db.Column('address', db.Integer, db.ForeignKey('address.id'))
)
class Person(db.Model):
__tablename__ = "person"
id = db.Column(Integer, primary_key=True)
name = db.Column(String, nullable=False)
addresses = db.relationship('Address', secondary=personaddress, backref=db.backref('person', lazy='dynamic'))
class Address(db.Model):
__tablename__ = "address"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
Requirements
Now I want to create a single page that contains forms to achieve the following:
add/edit/delete a person
add/edit/delete a address
add/edit/delete a relation between a person and an address
Important requirement: Using QuerySelectField, I can choose existing addresses for a person. But I want to add new addresses in the same form.
I've played around with model_form for the main models and subforms using FormField for the junction table but I just can't figure out how to update everything including the foreign key relations. The page should have a single submit button for all forms and subforms displayed.
Questions
How are the above requirements typically implemented in Flask?
Is this many-to-many scenario something that Django can handle more easily through its admin interface?
I have also encountered something similar earlier. I tried to solve it by using model_form, but it doesn't quite solve the problem of adding new entries dynamically, and I was having a hard time using it when dealing with relations.
Using the QuerySelectField in WTForms will only help you populating eg. an < select > with id, value pairs corresponding to the existing addresses. But it still renders to a regular html form in the template.
By using some sort of multiselect with the possibility to dynamically add new options in the frontend you can send additional addresses in the same form. The the endpoint will take care of creating new Addresses if they don't exist in the db.
The WTForm form would be:
from app import db
class PersonAddressForm(Form):
id = HiddenField('id')
name = StringField('Name')
addresses = QuerySelectField('Addresses',
query_factory=lambda: db.session.query(Address),
get_pk=lambda a: a.id, get_label=lambda a: a.name)
# Custom validate
def validate(self):
# ... custom validation
return True
And the route something like:
# ... this will be used to create and update a user
#route('create/<userid>', methods=["GET"])
def get_user_form(userid):
# ... Get the Person
user = Person()
if userid:
# ... if userid supplied, use existing Person object
user = Person.query.get(userid)
# ... Populate the form
person_form = PersonAddressForm(obj=user)
# ... return form
return render_template('somepage.html', form=person_form)
#route('create/<userid>', methods=["POST"])
def post_person_form(userid):
person_form = PersonAddressForm(request.form)
if person_form.validate():
# ... Get db object
person = db.session.query(Person).get(form.id)
# ... Add changes to the object from the form
person_form.populate_obj(obj=person_address)
# ... Get addresses
addresses = form.addresses.raw_data
# ... loop over and add to person
for address in addresses:
# Add or create an address
actual_address = db.session.query(Address).get(address.id)
# ... check if address is existing
if not actual_address:
# ... if address not existing, create new one
actual_address = Address(address.name)
db.session.add(actual_address)
# ... Append new or created address to person
person.addresses.append(actual_address)
# ... save changes to the db
db.session.commit()
# ... Update/Create complete
return redirect(url_for('get_users'))
else:
# ... form not valid, notify user
# ...
This will handle edit/create user and create Address. As well as create the relation between. To make it also support delete Address, change
person.addresses.append(actual_address)
to
person.addresses = list_of_actual_addresses
and change this in the person model (cascade='delete-orphan')
addresses = db.relationship('Address', secondary=personaddress, cascade='delete-orphan' backref=db.backref('person', lazy='dynamic'))
This will make the form update the entire address relation each time and the cascade will delete orphaned addresses. So the entire addresses list for a person would be updated each time the form is submitted.
When dealing with WTForms in templates i highly recommend using macros if you don't already. You would have to rewrite it to some degree, but check this out.
Hope this helps
I have a Friendship model with two related user objects associated with it. I would like to write a method that takes a user and returns a list of that user's friends. I was doing it via:
friends = Friendship.objects.filter(Q(user1=user) | Q(user2=user))
friends_list = [f.user1 if user == f.user2 else f.user2 for f in friends]
but this incurs a query for every user that is returned in the query set (e.g. hundreds of queries). I could write it via:
friends = Friendship.objects.select_related().filter(Q(user1=user) | Q(user2=user))
friends_list = [f.user1 if user == f.user2 else f.user2 for f in friends]
but this does an INNER JOIN on the user table. I could also write it via custom SQL,
friends = User.objects.raw("""
SELECT * FROM usertable WHERE id IN (SELECT
user1_id FROM friendstable WHERE user2_id=%d) OR id IN
(SELECT user2_id FROM lists_friendship WHERE user1_id=%d);
""" % (user.pk, user.pk))
but I was thinking there must a way to do it inside the ORM without all those extra lookups. I was trying to do something with id__in, but I couldn't find a way to get the user ids out of the friends query set.
Assuming you set up your Friendship model similar to the example below and use friend_set as the related name of the from_friend field in your model, getting a list of a users friends can be as simple as a basic list comprehension:
friends = [friendship.to_friend for friendship in user.friend_set.all()]
This would even allow you to access a user's friends in your template without having to pass that as a variable:
{% for friendship in user.friend_set.all %}
{{ friendship.to_friend.username }}
{% endfor %}
Your Friendship model would look like this:
class Friendship(models.Model):
from_friend = models.ForeignKey(
User, related_name='friend_set'
)
to_friend = models.ForeignKey(
User, related_name='to_friend_set'
)
def __unicode__(self):
return u'%s, %s' % (
self.from_friend.username,
self.to_friend.username
)
class Meta:
unique_together = (('to_friend', 'from_friend'), )
Here's a great article on building friend networking in Django:
http://www.packtpub.com/article/building-friend-networks-with-django-1.0
Note that you shouldn't have to check both from_friend and to_friend to get a users friend list. If you have a following/follower friendship model you only need all friendship instances with a from_friend = user, and if you have a double opt-in friendship model you could follow what Facebook does and add an instance for the invited friend as the from_friend once they accept the friend invite.
I am trying to understand the 1-to-many relationships in datastore; but I fail to understand how query and update the record of a user when the model includes ReferenceProperty. Say I have this model:
class User(db.Model):
userEmail = db.StringProperty()
userScore = db.IntegerProperty(default=0)
class Comment(db.Model):
user = db.ReferenceProperty(User, collection_name="comments")
comment = db.StringProperty()
class Venue(db.Model):
user = db.ReferenceProperty(User, collection_name="venues")
venue = db.StringProperty()
If I understand correctly, the same user, uniquely identified by userEmail can have many comments and may be associated with many venues (restaurants etc.)
Now, let's say the user az#example.com is already in the database and he submits a new entry.
Based on this answer I do something like:
q = User.all()
q.filter("userEmail =", az#example.com)
results = q.fetch(1)
newEntry = results[0]
But I am not clear what this does! What I want to do is to update comment and venue fields which are under class Comment and class Venue.
Can you help me understand how this works? Thanks.
The snippet you posted is doing this (see comments):
q = User.all() # prepare User table for querying
q.filter("userEmail =", "az#example.com") # apply filter, email lookup
- this is a simple where clause
results = q.fetch(1) # execute the query, apply limit 1
the_user = results[0] # the results is a list of objects, grab the first one
After this code the_user will be an object that corresponds to the user record with email "az#example.com". Seing you've set up your reference properties, you can access its comments and venues with the_user.comments and the_user.venues. Some venue of these can be modified, say like this:
some_venue = the_user.venues[0] # the first from the list
some_venue.venue = 'At DC. square'
db.put(some_venue) # the entry will be updated
I suggest that you make a general sweep of the gae documentation that has very good examples, you will find it very helpful:
http://code.google.com/appengine/docs/python/overview.html
** UPDATE **: For adding new venue to user, simply create new venue and assign the queried user object as the venue's user attribute:
new_venue = Venue(venue='Jeferson memorial', user=the_user) # careful with the quoting
db.put(new_venue)
To get all Comments for a given user, filter the user property using the key of the user:
comments = Comment.all().filter("user =", user.key()).fetch(50)
So you could first lookup the user by the email, and then search comments or venues using its key.