Field bindings in peewee - sql

How I can link fields in peewee as in sqlalchemy:
parent_id = Column(Integer, ForeignKey("parent.id"))
How to convert this code to peewee?

The very first document in the documentation describes creating a foreign key relationship. Instead of taking the time to write your question and post it here, you could have read that doc. I'm linking it here: http://docs.peewee-orm.com/en/latest/peewee/quickstart.html#model-definition
Example models:
from peewee import *
db = SqliteDatabase('people.db')
class Person(Model):
name = CharField()
birthday = DateField()
class Meta:
database = db # This model uses the "people.db" database.
class Pet(Model):
owner = ForeignKeyField(Person, backref='pets')
name = CharField()
animal_type = CharField()
class Meta:
database = db # this model uses the "people.db" database
For a self-referential foreign key: http://docs.peewee-orm.com/en/latest/peewee/models.html#self-referential-foreign-keys
Example:
class Category(Model):
name = CharField()
parent = ForeignKeyField('self', null=True, backref='children')

Related

How to remove SQLAlchemy Many-To-Many Orphans from database?

Context
I have a simple MySQL database written with SQLAlchemy. The following are my two models, Subreddit and Keyword, that have a many-to-many relationship, along with their association table:
subreddits_keywords = db.Table('subreddits_keywords', db.Model.metadata,
db.Column('subreddit_id', db.Integer, db.ForeignKey('subreddits.id', ondelete='CASCADE')),
db.Column('keyword_id', db.Integer, db.ForeignKey('keywords.id', ondelete='CASCADE')),
)
class Subreddit(db.Model, JsonSerializer):
__tablename__ = 'subreddits'
id = db.Column(db.Integer, primary_key=True)
subreddit_name = db.Column(db.String(128), index=True)
# Establish a parent-children relationship (subreddit -> keywords).
keywords = db.relationship('Keyword', secondary=subreddits_keywords, backref='subreddits', cascade='all, delete', passive_deletes=True, lazy='dynamic')
// ...
class Keyword(db.Model, JsonSerializer):
__tablename__ = 'keywords'
id = db.Column(db.Integer, primary_key=True)
keyword = db.Column(db.String(128), index=True)
// ...
As test data, I've created the following data set:
Subreddit:
test_subreddit
Keywords:
test_keyword1
test_keyword2
test_keyword3
In other words, test_subreddit.keywords should return [test_keyword1, test_keyword2, test_keyword3].
Problem
When I remove test_subreddit, test_keyword1, test_keyword2, test_keyword3 still persist in the database.
I understand that with many-to-many relationships, there is technically no parent so cascade's technically will not work according to this post:
https://stackoverflow.com/a/803584/10426919.
What I've Tried
I followed this link: https://github.com/sqlalchemy/sqlalchemy/wiki/ManyToManyOrphan.
This link provides a library function that should fix my exact problem.
However, the function does not work when integrated into my Model file in the following ways:
Method #1:
from app.extensions import db
from werkzeug.security import generate_password_hash, check_password_hash
from sqlalchemy.inspection import inspect
from sqlalchemy_utils import auto_delete_orphans <------ # library
subreddits_keywords = db.Table('subreddits_keywords', db.Model.metadata,
db.Column('subreddit_id', db.Integer, db.ForeignKey('subreddits.id', ondelete='CASCADE')),
db.Column('keyword_id', db.Integer, db.ForeignKey('keywords.id', ondelete='CASCADE')),
)
class Subreddit(db.Model, JsonSerializer):
__tablename__ = 'subreddits'
id = db.Column(db.Integer, primary_key=True)
subreddit_name = db.Column(db.String(128), index=True)
# Establish a parent-children relationship (subreddit -> keywords).
keywords = db.relationship('Keyword', secondary=subreddits_keywords, backref='subreddits', cascade='all, delete', passive_deletes=True, lazy='dynamic')
// ...
class Keyword(db.Model, JsonSerializer):
__tablename__ = 'keywords'
id = db.Column(db.Integer, primary_key=True)
keyword = db.Column(db.String(128), index=True)
// ...
auto_delete_orphans(Subreddit.keywords) <------ # Library function
However, this function does not seem to do anything. There is no error that is output to help guide me towards the right direction. When I check my database in MySQL workbench, the Subreddit, test_subreddit, is deleted, but the keywords [test_keyword1, test_keyword2, test_keyword3] are still in the database under the Keywords table.
Method #2:
I tried integrating the actual function, that the library function is based on, into my code as well:
from app.extensions import db
from werkzeug.security import generate_password_hash, check_password_hash
from sqlalchemy.inspection import inspect
from sqlalchemy_utils import auto_delete_orphans
# for deleting many-to-many "orphans".
from sqlalchemy import event, create_engine
from sqlalchemy.orm import attributes, sessionmaker
subreddits_keywords = db.Table('subreddits_keywords', db.Model.metadata,
db.Column('subreddit_id', db.Integer, db.ForeignKey('subreddits.id', ondelete='CASCADE')),
db.Column('keyword_id', db.Integer, db.ForeignKey('keywords.id', ondelete='CASCADE')),
)
class Subreddit(db.Model, JsonSerializer):
__tablename__ = 'subreddits'
id = db.Column(db.Integer, primary_key=True)
subreddit_name = db.Column(db.String(128), index=True)
# Establish a parent-children relationship (subreddit -> keywords).
keywords = db.relationship('Keyword', secondary=subreddits_keywords, backref='subreddits', cascade='all, delete', passive_deletes=True, lazy='dynamic')
// ...
class Keyword(db.Model, JsonSerializer):
__tablename__ = 'keywords'
id = db.Column(db.Integer, primary_key=True)
keyword = db.Column(db.String(128), index=True)
// ...
engine = create_engine("mysql://", echo=True)
Session = sessionmaker(bind=engine)
#event.listens_for(Session, 'after_flush')
def delete_tag_orphans(session, ctx):
# optional: look through Session state to see if we want
# to emit a DELETE for orphan Tags
flag = False
for instance in session.dirty:
if isinstance(instance, Subreddit) and \
attributes.get_history(instance, 'keywords').deleted:
flag = True
break
for instance in session.deleted:
if isinstance(instance, Subreddit):
flag = True
break
# emit a DELETE for all orphan Tags. This is safe to emit
# regardless of "flag", if a less verbose approach is
# desired.
if flag:
session.query(Keyword).\
filter(~Keyword.subreddits.any()).\
delete(synchronize_session=False)
Again, the keywords persisted despite being attached to no parent.
What I'm trying to accomplish
When children in the database no longer have a parent, I would like them to be removed from the database. What am I doing wrong?
Rather than using auto_delete_orphans, I created a method that I can call when I want to delete children. This method checks the child in question, and sees if it has any parents. If it does have a parent, we leave it be, but if it does not have a parent, we then delete the children.
Here is how I implemented this method, given that a Subreddit is a parent and a Keyword is a child of Subreddit.
def check_for_keyword_orphans(keyword):
# check if each keyword has an associated subreddit
if len(keyword.subreddits) == 0:
db.session.delete(keyword)
return True # keyword deleted
else:
return False # keyword has an associated subreddit
And here is how I used the method in my API route:
keywords = subreddit.keywords
for keyword in keywords:
check_for_keyword_orphans(keyword)
db.session.commit()

How can I create a Schema in Marshmallow to reverse-nest queried data?

Sorry if this sounds silly, but i'm trying to get all the books for an author. This is what I have:
class Author(db.Model):
__tablename__='author'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
class Book(db.Model):
__tablename__='book'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
author_id = db.Column(db.Integer, ForeignKey('author.id'))
author_rel = relationship('Author', lazy='joined'), backref='book')
and I have my schemas:
class BookSchema(Schema):
id = fields.Int()
name= field.Str()
class BookSchema(Schema):
id = fields.Int()
title = field.Str()
author = fields.Nested(Author)
So I can retrieve the books with the author and the authors.
What I need here is to add a nested field with all the books of each author... I've been trying, but failing to do so. Is there an automatic way to get this?
I'm trying to join the tables in the query, but also failing to do it:
session.query(Author, Book).join(Book, Author.id == Book.author_id).all()
This gives me a (Author, Book) tuple, and I cannot map that into a concise json... How could I do that?
EDIT:
Ok, so apparently I didn't understand the concept of a relationship haha I could avoid all this trouble by simply adding a relationship to my Author entity:
class Author(db.Model):
__tablename__='author'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
books = relationship('Book', lazy='joined', backref='author_book')
class Book(db.Model):
__tablename__='book'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
author_id = db.Column(db.Integer, ForeignKey('author.id'))
author = relationship('Author', lazy='joined', backref='book_author')
Then I could just populate my marshmallow schema normally and be happy
==================================================
The solution I used was:
session = Session()
author_objects = session.query(Author).all()
schema = AuthorSchema(many=True)
author_list = schema.dump(author_objects).data
book_objects = session.query(Book).all()
schema = BookSchema(many=True)
book_list = schema.dump(book_objects).data
for index, author in enumerate(author_list):
author_list[index]['books'] = [book for book in book_list if book['author_id'] == author['id']]
session.close()
return jsonify(author_list)
This feels kinda manual for me, I think there should be a better way to do this automatically using schemas. It's, after all, based on a relationship that exists.
This works, but would be slow for long lists... I preferred to do this using sql-alchemy + marshmallow directly...
Ideas?

How to set a fixed value of particular column to user in POST API in Django?

I am using DRF for creating API, using basic APIView. Below are the models and views.
models.py
from django.db import models
from django.utils import timezone
class PizzaOrder(models.Model):
name = models.CharField(max_length=120)
size = models.CharField(default='MEDIUM')
customer_name = models.CharField(max_length=120)
customer_address = models.TextField()
ordered_time = models.DateTimeField(default=timezone.now)
Now, I need two things in that.
First - I want to set STATUS as 'open' in the database while ordering pizza. User isn't able to see the STATUS column while saving.
Second - I want to get all orders, but it should show STATUS to the user now.
view.py
class PizzaOrderView(APIView):
def get(self, request):
orders = PizzaOrder.objects.all()
serializer = ArticleSerializer(orders, many=True)
return Response({"orders": serializer.data})
def post(self, request):
orders = request.data.get('orders')
serializer = ArticleSerializer(data=orders)
if serializer.is_valid(raise_exception=True):
article_saved = serializer.save()
What should I do to get my all two requirements?
First of all if you want to save status in the database you must include it as one of the fields in your model. I would also suggest adding the choices enumeration so that nothing unwanted can end up in database as a status (note that you can add any other choice besides open and closed):
class PizzaOrder(models.Model):
...
OPEN = 'open'
CLOSED = 'closed'
TYPE_CHOICES = (
(OPEN, 'Open'),
(CLOSED, 'Closed'),
)
status = models.CharField(max_length=120, choices=STATUS_CHOICES, default=OPEN)
Next thing to know is that the way data is presented to the user depends mostly on your serializers (would be useful if you could include those in your question aswell). So for serializing the PizzaOrder data you can use two different serializers (one which includes the status field, and one which does not):
class PizzaOrderSerializer(serializers.ModelSerializer):
class Meta:
model = PizzaOrder
fields = '__all__'
class PizzaOrderWithoutStatusSerializer(serializers.ModelSerializer):
class Meta:
model = PizzaOrder
fields = ['name', 'size', 'customer_name', 'customer_address', 'ordered_time']
you can user one serializer just just using the one serializer
class PizzaOrderSerializer(serializers.ModelSerializer):
class Meta:
model = PizzaOrder
fields = '__all__'
read_only_fields = ('status',)

Django Model Design (Big model vs multiple)

I have a question about designing my models. Suppose I have a following model:
class Comment(models.Model):
who = models.ForeignKey(User, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
text = models.CharField(max_length=1000)
likes = models.IntegerField(default=0)
parent_comment = models.ForeignKey('self', on_delete=models.CASCADE, null=True, related_name='child_comments')
Now I would like models for multiple topics (ShoppingList, Games,...). I came to two possible solutions and need help with deciding more suitable one.
1) Make Comment abstract and extend it for every new model wanted.
class ShoppingListComment(Comment):
shopping_list = models.ForeignKey(ShoppingList, related_name='shopping_comments', on_delete=models.CASCADE)
I could then query this game comments with something like:
ShoppingListComment.objects.all()
2) Add extra nullable Foreing keys directly to comment:
class BigCommentModel(models.Model):
who = models.ForeignKey(User, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
text = models.CharField(max_length=1000)
likes = models.IntegerField(default=0)
parent_comment = models.ForeignKey('self', on_delete=models.CASCADE, null=True, related_name='child_comments')
shopping_list = models.ForeignKey(ShoppingList, related_name='shopping_comments', on_delete=models.CASCADE, null=True),
game = models.ForeignKey(Game, related_name='game_comments', on_delete=models.CASCADE, null=True)
I could then query this game comments with something lile:
BigCommentModel.objects.filter(game__isnull=False)

Odoo, can't have two different siblings objects in the same view

I have a class named detail_base, and two other classes name flight_detail and tour_detail, the last two classes inherits from the first one, like this:
class DetailBase(models.Model):
_name = 'detail_base'
fee = fields.Monetary('Fee')
passenger = fields.Char('Passenger')
class FlightDetail(models.Model):
_name = 'flight_detail'
_inherits = 'detail_base'
passport = fields.Char('Passport')
class TourDetail(models.Model):
_name = 'tour_detail'
_inherits = 'detail_base'
age = fields.Integer('Tourist Age')
The problem is when I call the flight_detail and tour_detail in the same view, the browser can't handle the common attributes of both classes, If I assign 5 to tour_detail.fee, that number will be stored into flight_detail.fee.
It seems the problem is related to the attributes with the same name of different objects being siblings.
I will appreciate any help.
You should either use _inherit
class DetailBase(models.AbstractModel):
_name = 'detail_base'
fee = fields.Monetary('Fee')
passenger = fields.Char('Passenger')
class FlightDetail(models.Model):
_name = 'flight_detail'
_inherit = 'detail_base'
passport = fields.Char('Passport')
class TourDetail(models.Model):
_name = 'tour_detail'
_inherit = 'detail_base'
age = fields.Integer('Tourist Age')
which should either create 3 database tables (detail_base as Model) or 2 database tables (AbstractModel).
Or you use _inherits like:
class DetailBase(models.Model):
_name = 'detail_base'
fee = fields.Monetary('Fee')
passenger = fields.Char('Passenger')
class FlightDetail(models.Model):
_name = 'flight_detail'
_inherits = {'detail_base': 'base_id'}
passport = fields.Char('Passport')
base_id = fields.Many2one('detail_base', required=True, ondelete='cascade')
class TourDetail(models.Model):
_name = 'tour_detail'
_inherit = {'detail_base': 'base_id'}
age = fields.Integer('Tourist Age')
base_id = fields.Many2one('detail_base', required=True, ondelete='cascade')
That will create 3 tables, and fee and passenger will be stored in detail_base table. Odoo will get it from there, because it is a sort of delegation inheritence.
use inherit:
1- inherit without _name :
inherit = 'model.name'
new_field = fields...
this add this field to the model.name
2- inherit with _name:
inherit = 'model.name'
_name = 'new.model'
here will create a new tabel in database with the same structure of model.name.
inherits: The delegation inheritance.
best example is res.users and res.partners user is a partener so when we create a res.users record we must create a res.partener that hold commun field like name, email, address ... and information related to users like passowrd and login are stored in res.users model and with type of inheritence you can access field of res.partener directly without having to create a related field. you can do user_record.name or .email or .address this will not be a problem.
i like to think of it as one2one relation.
_inherits = {model.name : many2one_field_id }
_name = 'new.model'
# m2o field should be required and ondelete = cascade
many2one_field_id = fields.Many2one('model.name', string='Label', required=True, ondelete="cascade")
so when you create a record of new.model all field that are in model.name will be stored in model.name.