create Self Referencing Table using peewee - python

I am failing to find a way I can create a self-referencing table using peewee. I am trying to create an entity similar to one on this article.
I have tried this solution here and it doesn't seem to give me the results that I want.
class Customer(Model):
name = TextField()
class CustomerDepartment(Model):
refid = ForeignKeyField(Customer, related_name='customer')
id = ForeignKeyField(Customer, related_name='department')

These are documented here: http://docs.peewee-orm.com/en/latest/peewee/models.html#self-referential-foreign-keys
class Department(BaseModel):
parent = ForeignKeyField('self', null=True, backref='children')
name = CharField()
Example use:
root = Department.create(name='root')
d1 = Department.create(parent=root, name='Dept 1')
# etc.

Related

django-tables 2 M2M field not shown

I am trying to show a M2M field in a django-table2 as seen in Django-tables2: How to use accessor to bring in foreign columns? and Accessing related models with django-tables2
Using: foreigncolumn = tables.Column(accessor='foreignmodel.foreigncolumnname'), I only see a '--'...
# The models:
class Organism(models.Model):
species_name = models.CharField(max_length=200)
strain_name = models.CharField(max_length=200)
eukaryotic = models.BooleanField(default=True)
lipids = models.ManyToManyField('Lipid',blank=True)
class Lipid(models.Model):
lm_id = models.CharField(max_length=100)
common_name = models.CharField(max_length=100,blank=True)
category = models.CharField(max_length=100,blank=True)
#The tables
class OrganismTable(tables.Table):
name = tables.LinkColumn('catalog:organism-detail', text=lambda record: record.species_name, args=[A('pk')])
lp = tables.Column(accessor='Lipid.common_name')
class Meta:
model = Organism
sequence = ['name','lp']
exclude = ['id','species_name']
Any idea what I'm doing wrong?
This does not work so easily for ManyToManyFields because of the simple way Accessor works. You could display the repr of the related QuerySet via 'lipids.all' but that does not seem sufficient here. You can, however, add a property (or method) to your Organism model and use it in the accessor. This way, you can display any custom information related to the instance:
class Organism(models.Model):
# ...
#property
def lipid_names(self):
return ', '.join(l.common_name for l in self.lipids.all()) # or similar
class OrganismTable(tables.Table):
# ...
lp = tables.Column(accessor='lipid_names')
I would recommend then to add a prefetch_related('lipids') to the Organism QuerySet that you pass to the table for better performance.

How to override create_table() in peewee to create extra history?

Following this SO answer and using the (excellent) Peewee-ORM I'm trying to make a versioned database in which a history of a record is stored in a second _history table. So when I create a new using the create_table() method I also need to create a second table with four extra fields.
So let's say I've got the following table:
class User(db.Model):
created = DateTimeField(default=datetime.utcnow)
name = TextField()
address = TextField()
When this table is created I also want to create the following table:
class UserHistory(db.Model):
created = DateTimeField() # Note this shouldn't contain a default anymore because the value is taken from the original User table
name = TextField()
address = TextField()
# The following fields are extra
original = ForeignKeyField(User, related_name='versions')
updated = DateTimeField(default=datetime.utcnow)
revision = IntegerField()
action = TextField() # 'INSERT' or 'UPDATE' (I never delete anything)
So I tried overriding the Model class like this:
class Model(db.Model):
#classmethod
def create_table(cls, fail_silently=False):
db.Model.create_table(cls, fail_silently=fail_silently)
history_table_name = db.Model._meta.db_table + 'history'
# How to create the history table here?
As you can see I manage to create a variable with the history table name, but from there I'm kinda lost.
Does anybody know how I can create a new table which is like the original one, but just with the added 4 fields in there? All tips are welcome!
Maybe something like this:
class HistoryModel(Model):
#classmethod
def create_table(cls...):
# Call parent `create_table()`.
Model.create_table(cls, ...)
history_fields = {
'original': ForeignKeyField(cls, related_name='versions'),
'updated': DateTimeField(default=datetime.utcnow),
'revision': IntegerField(),
'action': TextField()}
model_name = cls.__name__ + 'History'
HistoryModel = type(model_name, (cls,), history_fields)
Model.create_table(HistoryModel, ...)
Also note you'll want to do the same thing for create_indexes().
I'd suggest creating a property or some other way to easily generate the HistoryModel.

SQLite3 OperationalError: Table XYZ has no column named ABC

I am new to peewee, so please forgive me if this is a stupid question. I have searched on Google and in the peewee cookbook, but found no solution so far.
So, I have the following models to four of my DB tables:
class games_def(Model):
id = PrimaryKeyField()
name = TextField()
class Meta:
database = dbmgr.DB
class users_def(Model):
id = PrimaryKeyField()
first_name = TextField()
last_name = TextField()
class Meta:
database = dbmgr.DB
class sessions(Model):
id = PrimaryKeyField()
game = ForeignKeyField(games_def, related_name = 'sessions')
user = ForeignKeyField(users_def, related_name = 'sessions')
comment = TextField()
class Meta:
database = dbmgr.DB
class world_states(Model):
session = ForeignKeyField(sessions)
time_step = IntegerField()
world_state = TextField()
class Meta:
database = dbmgr.DB
Using these models I connect to an SQLite3 DB via peewee, which works fine.
After the connection has been established I do the following in my main Python code:
models.world_states.create(session = 1, time_step = 1)
However, this gives me the following error:
sqlite3.OperationalError: table world_states has no column named session_id
That is basically correct, the table world_state does indeed not contain such a column.
However, I cannot find any reference to "session_id" in my code at all.
Whe does peewee want to use that "session_id" colum name?
Do I miss something essentially here?
When you specify a ForeignKeyField() peewee expects to use a column ending in _id based on their own name. Your wold_states.session field thus results in an column named session_id.
You can override this by setting db_column for that field:
class world_states(Model):
session = ForeignKeyField(sessions, db_column='session')

SQLAlchemy equivalent of Django ORM's relationship-spanning filter

This example is from the Django documentation.
Given the (Django) database model:
class Blog(models.Model):
name = models.CharField(max_length=100)
class Entry(models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=255)
body_text = models.TextField()
In Django I can use:
Entry.objects.filter(blog__name__exact='Beatles Blog')
to get all Entry objects for blogs with the specified name.
Question: What is the equivalent SQLAlchemy statement, given the model definition below?
class Blog(Base):
__tablename__ = "blog"
id = Column(Integer, primary_key=True)
name = Column(Unicode(100))
class Entry(Base):
__tablename__ = "entry"
id = Column(Integer, primary_key=True)
blogid = Column(Integer, ForeignKey(Blog.id))
headline = Column(Unicode(255))
body_text = Column(UnicodeText)
blog = relationship(Blog, backref="entries")
EDIT
I believe there are two ways to accomplish this:
>>> q = session.query
>>> print q(Entry).join(Blog).filter(Blog.name == u"One blog")
SELECT entry.id AS entry_id, entry.blogid AS entry_blogid, entry.headline AS entry_headline, entry.body_text AS entry_body_text
FROM entry JOIN blog ON blog.id = entry.blogid
WHERE blog.name = ?
>>> print q(Entry).filter(Entry.blog.has(Blog.name == u"One blog"))
SELECT entry.id AS entry_id, entry.blogid AS entry_blogid, entry.headline AS entry_headline, entry.body_text AS entry_body_text
FROM entry
WHERE EXISTS (SELECT 1
FROM blog
WHERE blog.id = entry.blogid AND blog.name = ?)
# ... and of course
>>> blog = q(Blog).filter(Blog.name == u"One blog")
>>> q(Entry).filter(Entry.blog == blog)
A few more questions:
Are there other ways to accomplish this using SQLAlchemy than the ones above?
Would it not make sense if you could do session.query(Entry).filter(Entry.blog.name == u"One blog") in many-to-one relationships?
What SQL does Django's ORM produce in this case?
I also always dreamed of having Django-like "magic joins". I'm familiar with
sqlalchemy-django-query it, I found that it's not powerful enough for my tasks.
That's why I created
https://github.com/absent1706/sqlalchemy-mixins#django-like-queries.
It works similar to sqlalchemy-django-query but has more additional features (here's a comparison). Also it's well tested and documented.
How about:
session.query(model.Entry).join((model.Blog, model.Entry.blogid==model.Blog.id)).filter(model.Blog.name=='Beatles Blog').all()
Already late to the party, but i stumbled across this:
https://github.com/mitsuhiko/sqlalchemy-django-query/blob/master/sqlalchemy_django_query.py
This tries to build queries using the django notation.
From the docs: Post.query.filter_by(pub_date__year=2008)

How can I do INNER JOIN in Django in legacy database?

Sorry for probably simple question but I'm a newby in Django and really confused.
I have an ugly legacy tables that I can not change.
It has 2 tables:
class Salespersons(models.Model):
id = models.IntegerField(unique=True, primary_key=True)
xsin = models.IntegerField()
name = models.CharField(max_length=200)
surname = models.CharField(max_length=200)
class Store(models.Model):
id = models.IntegerField(unique=True, primary_key=True)
xsin = models.IntegerField()
brand = models.CharField(max_length=200)
So I suppose I can not add Foreign keys in class definitions because they change the tables.
I need to execute such sql request:
SELECT * FROM Salespersons, Store INNER JOIN Store ON (Salespersons.xsin = Store.xsin);
How can I achieve it using Django ORM?
Or I'm allowed to get Salespersons and Store separately i.e.
stores = Store.objects.filter(xsin = 1000)
salespersons = Salespersons.objects.filter(xsin = 1000)
Given your example query, are your tables actually named Salespersons/Store?
Anyway, something like this should work:
results = Salespersons.objects.extra(tables=["Store"],
where=["""Salespersons.xsin = Store.xsin"""])
However, given the names of the tables/models it doesn't seem to me that an inner join would be logically correct. Unless you always have just 1 salesperson per store with same xsin.
If you can make one of the xsin fields unique, you can use a ForeignKey with to_field to generate the inner join like this:
class Salespersons(models.Model):
xsin = models.IntegerField(unique=True)
class Store(models.Model):
xsin = models.ForeignKey(Salespersons, db_column='xsin', to_field='xsin')
>>> Store.objects.selected_related('xsin')
I don't see why you can't use the models.ForeignKey fields even if the database lacks the constraints -- if you don't explicitly execute the SQL to change the database then the tables won't change. If you use a ForeignKey then you can use Salespersons.objects.select_related('xsin') to request that the related objects are fetched at the same time.

Categories