Sqlalchemy searching across multiple classes with shared attributes - python

My problem is as follows:
I have 3 classes that all inherent attributes from an abstract class - id, entrytime, expirytime, owner. These classes also have other attributes that are unique to themselves, so they are stored in different tables.
I wish to query over all these classes and search by the shared attributes, for example a select search where expirytime > datetime.now without having to iterate through each class and use a query.filter function.
I thought this would work by querying a class with __abstract__ = True but sqlalchemy doesn't like to query without having a table associated with said class. I have considered joined table inheritance but that seems overly complex for this since I do not need to store the abstract class in a separate table, only use it to query shared attributes.

Related

SQLAlchemy: how to create a relationship programmatically

I'd like to create a 1:n relationship between two tables dynamically. My DB model is mapped via SQLAlchemy but due to some special features of my application I can not use the default declarative way.
E.g.
class Foo(Base):
id = Column(Integer, autoincrement=True, primary_key=True)
flag = Column(Boolean)
class Bar(Base):
id = Column(Integer, autoincrement=True, primary_key=True)
foo_id = Column(Integer, ForeignKey('foo.id'))
# declarative version:
# foo = relationship(Foo)
So I want to add relationship named "foo" to the mapped class "Bar" after Bar was defined and SQLAlchemy did its job of defining a mapper etc.
Update 2017-09-05: Why is this necessary for me? (I thought I could omit this because I think it mostly distracts from the actual problem to solve but since there were comments abouts it...)
First of all I don't have a single database but hundreds/thousands. Data in old databases must not be altered in any way but I want a single source code to access old data (even though data structure and calculation rules change significantly).
Currently we use multiple model definitions. Later definitions extend/modify previous ones. Often we manipulate SQLAlchemy models dynamically. We try not to have code in mapped classes because we think it will be much harder ensuring correctness of that code after changing a table many times (code must work in every intermediate step).
In many cases we extend tables (mapped classes) programatically in model X after it was initially defined in model X-1. Adding columns to an existing SQLAlchemy ORM class is manageable. Now we are adding a new reference column an existing table and a relationship() provides a nicer Python API.
Well, my question above is again a nice example of SQLAlchemy's super powers (and my limited understanding):
Bar.__mapper__.add_property('foo', relationship('Foo'))
Likely I was unable to get this working initially because some of my surrounding code mixed adding relationships and columns. Also there is one important difference to declaring columns:
Column('foo', Integer)
For columns the first parameter can be the column name but you can not use this for relationships. relationship('foo', 'Foo') triggers exceptions when passing it to .add_property().

How to reuse sqlalchemy declarative models in different database schema

I am working on implementing sets of sqlalchemy declarative models for multiple database schemas. These database schemas are highly similar in table design. I would like to be able to reuse as much of the common model code (column definitions, relations etc.) as possible across the sets of models for each schema.
(Please note, the schemas in question are 99% identical.)
As a small example:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Integer, String
Base = declarative_base()
class Person(Base):
__tablename__ = 'person'
name = Column(String, primary_key=True)
age = Column(Integer)
If I now want to introduce a new attribute Person column 'height' that only exists in one schema and not the other, how can I go about defining it whilst reusing the existing Person column definitions?
So far, I have tried using declarative mixins to define the common functionality and then mix these into classes that inherit from different schema-specific bases. However, this is unsatisfactory because even the majority of identical models have to be declared as mixins, and a declarative class declared once for schema (inheriting from the appropriate base and the mixin). Given that the schemas are similar this seems like a lot of boilerplate code.
Is there a better way to do it?

Django model polymorphism without Multi-Table Inheritance and additional JOIN

I’m quite new to Django and I’m trying to implement polymorphism inside a Django model, but I can’t see how to do. Before going on I have to say I’ve already tried django-model-utils and django-polymorphism, but they don’t do exactly what I’m looking for.
I have a model called Player, each player has a Role and each Role has different behaviours (i.e. their methods return different values):
class Player(models.Model):
username=models.TextField()
role=models.ForeignKey(Role) #Role is another model with a field called ’name'
def allow_action(self)
#some stuff
class RoleA():
def allow_action(self):
#some specific stuff
class RoleB():
pass
I want that every time I retrieve any instance of Player (in example through Player.objects.filter(…)) every instances has the allow_action() method overwritten by the custom one defined inside the specific class (RoleA, RoleB, etc…) or use the default method provided in Player if the related subclass has no method called with the same name (RoleA, RoleB, etc... are the same role name stored in Player.role.name).
CONSTRAINTS:
Since subclasses (RolaA, RoleB, etc…) do not add new field but only overwrite methods all data have to be stored inside Player’s table, so I don’t want to use Django Multi-Table Inheritance but something more similar to Proxies.
I don’t want to perform additional JOIN to determine specific subclass type since all informations needed are stored inside Player’s table.
I think that this is a standard polymorphism pattern but I don’t see how to implement it in Django using the same table for all players (I've already implemented this polymorphism but not linked to a Django model). I’ve seen Django has a kind of inheritance called “Proxy” but it doesn’t allow to make queries like Player.objects.filter(…) and get instances with method overwritten by custom ones (or at least this is what I understood).
Thanks in advance.
Disclaimer: I've not used django-polymorphic, and this code is based on 5 minutes spent scanning the docs and is entirely untested but I'll interested to see if it works:
from polymorphic import PolymorphicModel
class Role(PolymorphicModel):
name = models.CharField()
class RoleA(Role):
def allow_action(self):
# Some specific stuff...
class RoleB(Role):
pass
class Player(models.Model):
username=models.TextField()
role=models.ForeignKey(Role) #Role is another model with a field called ’name'
def allow_action(self)
if callable(getattr(self.role, "allow_action", None):
self.role.allow_action()
else:
# default action...
Now I believe you should be able to create an instance of Role, RoleA, or RoleB and have Player point to it in the foreign key. Calling allow_action() on an instance of Player will check to see if the instance of Role (or RoleA, RoleB etc) has a callable attribute allow_action() and if so, it will use that, otherwise it will use the default.

Django: composite fields or embedded classes (like JPA)?

Suppose you have to model several classes that should have composite properties like dimensions (width and height) or phone number (prefix, number and extension).
In Java (using JPA 2) I'd create a Dimensions class and annotate it with #Embeddable. This causes Dimension's fields (e.g. width and height) to be embedded into every class that declares a property of type Dimensions.
How do you model these with Django while avoiding code duplication? It doesn't make sense to create a separate Dimensions model and reference it with a ForeignKey field. And the classes do not have enough in common to justify model inheritance.
I think you might be over-thinking inheritance. Inheritance is and is actually the recommended method to composite models. The following is an example of how properly use model inheritance in Django:
class PhoneModelBase(model.Model):
phone = models.CharField(max_length=16)
...
class Meta:
abstract = True
class PhoneModel(PhoneModelBase):
# phone is here without typing it
# the only restriction is that you cannot redefine phone here
# its a Django restriction, not Python restriction
# phone = models.CharField(max_length=12) # <= This will raise exception
pass
So what this does is it creates a model PhoneModelBase, however the key here is that it uses class Meta with abstract=True.
Here is more of behind the scenes of what is going on and some explanation of some of the Python concepts. I assume that you are not aware of them since you mentioned Java in the question. These Python concepts are actually rather confusing concepts so my explanation is probably not full, or even confusing, so if you will not follow, don't mind. All you have to know is to use abstact = True. Here is the official doc: https://docs.djangoproject.com/en/dev/topics/db/models/#abstract-base-classes.
Meta attribute within PhoneModelBase is just that, an attribute. It is the same as any other attribute within the class, except its a class instance (remember that in Python classes and functions are first order members). In addition, Python has this thing called __metaclass__ which you can add to you classes. __metaclass__ defines a way of how an instance of a class is build. More about that here. Django uses these in how it creates model class instances.
So to create the PhoneModelBase class, the following is a rough outline:
When an instance of the PhoneModelBase class (the class itself, not instance of class - PhoneModelBase()) is being created, the __metaclass__ which comes from model.Model due to inheritance takes over the creation process
Within the __metaclass__, Python calls the function which creates the actual class instance and passes to it all of the fields from the class you are trying to create - PhoneModelBase. That will include phone, Meta and any other fields you define
It sees the Meta attribute and then it starts analyzing its attributes. According to the values of these attributes, Django will change the behavior of the model
It sees the abstract attribute and then changes the logic of the class its trying to create - PhoneModelBase by not storing it in db
So then the PhoneModelBase, even though its definition looks very similar to a regular model, its not a regular model. It is just an abstract class which is meant to be used as composite in other models.
When other models inherit from PhoneModelBase, their __metaclass__ will copy the attributes from the base model as if you manually typed those attributes. It will not be a foreign key on anything like that. All of the inherited attributes are going to be a part of the model and will be in the same table.
Hopefully all of this makes some sense. If not, all you have to remember is just to use Meta class with abstract = True.
EDIT
As suggested in the comment, you can also inherit from multiple base classes. So you can have PhoneModelBase, DimensionsModelBase, and then you can inherit from both of these (or more), and all of the attributes from all base classes will be present in your model.

Can I find all of a certain base model in App Engine?

Given a class-like relationship:
class A(db.Model):
pass
class B(A):
pass
Can I get all of the base class? The query:
models.A.all().fetch(1)
returns an empty list.
The datastore doesn't natively support this sort of polymorphism - but you can use the polymodel class to do this. Just inherit from PolyModel instead of Model and things will behave more or less as you expect them to.
The datastore does not record inheritance, per se: it stores the B entities as being of kind B. You can get all (direct, proper) subclasses of A with A.__subclasses__() (if you want indirect subclasses as well you'll need to do the same with each of these, and so forth, recursively, until you stop getting subclasses), and perform all the .all queries on them.

Categories

Resources