How do I tell SQLAlchemy to automatically reflect basic Foreign Key references as references to other ORM objects and not integer fields?
In both SQLAlchemy and it's SqlSoup, table columns are reflected automatically and relations can be defined manually:
class User(Base):
__table__ = metadata.tables['users']
loan = relation(Loans)
...
You can define relationships on SqlSoup classes:
>>> db.users.relate('loans', db.loans)
Try this magic )
Works for simple FK relations, and without db schemes
from sqlalchemy import create_engine, MetaData
from sqlalchemy.orm import mapper, relation
engine = create_engine("sqlite://", echo=True)
engine.execute('''
create table foo (
id integer not null primary key,
x integer
)''')
engine.execute('''
create table bar (
id integer not null primary key,
foo_id integer,
FOREIGN KEY(foo_id) REFERENCES foo(id)
)''')
metadata = MetaData()
metadata.reflect(bind=engine)
MAPPERS = {
}
repr_name = lambda t: '%s%s' % (t[0].upper(), t[1:])
for table in metadata.tables:
cls = None
# 1. create class object
cls_name = repr_name(str(table))
exec("""class %s(object): pass""" % cls_name)
exec("""cls = %s""" % cls_name)
# 2. collect relations by FK
properties = {}
for c in metadata.tables[table].columns:
for fk in c.foreign_keys:
name = str(fk.column).split('.')[0]
properties.update({
name: relation(lambda: MAPPERS[repr_name(name)]),
})
# 3. map table to class object
mapper(cls, metadata.tables[table], properties=properties)
MAPPERS.update({cls_name: cls})
if __name__ == '__main__':
from sqlalchemy.orm import sessionmaker
print 'Mappers: '
for m in MAPPERS.values():
print m
session = sessionmaker(bind=engine)()
foo = Foo()
foo.x = 1
session.add(foo)
session.commit()
print session.query(Foo).all()
bar = Bar()
bar.foo = foo
session.add(bar)
session.commit()
print session.query(Bar).all()
Related
I am creating an app in which I have a User entity and an Item entity that are related one to many.
I am using TORTOISE ORM, in a file with the path: app/models/user.py I have the following code:
from tortoise import fields
from app.models.base_class import Base
class User(Base):
name = fields.CharField(max_length = 50, nullable = False)
last_name = fields.CharField(max_length = 50, nullable = False)
email = fields.CharField(max_length = 128, unique = True, nullable = False)
password = fields.CharField(max_length = 128, nullable = False)
create_date = fields.DatetimeField(auto_now = True)
class Meta:
table = 'user_db'
Hello, I am creating an app in which I have a User entity and an Item entity that are related one to many.
I am using TORTOISE ORM, in a file with the path: app/models/user.py I have the following code:
And in the path: app/models/item.py I have the following code:
from tortoise.models import Model
from tortoise import fields
from app.models.base_class import Base
class Item(Base):
title = fields.CharField(max_length = 50, nullable = False)
description = fields.TextField(nullable = False)
weight = fields.DecimalField(max_digits=3)
cost = fields.DecimalField(max_digits=3)
url_image = fields.CharField(max_length = 255)
user_id = fields.ForeignKeyField(
'app.models.user.User',
'id',
on_delete = fields.CASCADE
)
My question is if I am correctly implementing the foreign key in the user_id field, is it correct to write the path 'app.models.user.User' for the model name parameter? Do I have to import User in item.py to be able to do this?
Or, on the other hand, should I write in this parameter is 'user_db', which will be the name of the User model table? I'm tangled up with this.
I cannot find anything about this type of relation (everything is about one-to-one, one-to-many or many-to-many). And even those look a little bit too complicated for what I need.
I have a table with tasks and a table with images. Multiple tasks can have the same image in order to save space (the image is not deleted when the task is deleted). I have an entity for the task
import android.graphics.Bitmap
import androidx.room.*
#Entity(tableName = "tasks")
class Task(
#PrimaryKey(autoGenerate = true)
val id: Long,
val imageId: Long = 0,
// this needs a foreign key to Image
val image: Image
)
and another one for the image
import androidx.room.ColumnInfo
import androidx.room.PrimaryKey
#Entity(tableName = "images")
class Image (
#PrimaryKey(autoGenerate = true)
val id: Long,
val title: String,
#ColumnInfo(typeAffinity = ColumnInfo.BLOB)
val data: ByteArray? = null
)
How do I add a foreignKey for the imageId column so that it points to an Image? Can I directly obtain an Image object as a member of Task without having to create another class?
You have to use one-to-many relationship here. There is an example from d.android.com. If you will do like that you will have a Task entity, an Image entity and data class TaskWithImage, just like in the example provided.
data class TaskWithImage(
#Embedded val task: Task,
#Embedded(prefix = "img_") val image: Image
)
#Query("""
SELECT * FROM tasks
INNER JOIN images as img_ ON tasks.id = img_.id
""")
fun getTasksWithImage(): List<TaskWithImage>
Error:
app_a.desc_id may not be NULL
I believe my problem is I'm not passing the id from formB to formA when I save. please please lead me to a solution for this problem.
Here's my view:
def form(request):
if request.method == 'GET':
formB = BForm()
formA = AForm()
return render(request,r'app/form.html',{'formA':formA,'formB':formB})
elif request.method == 'POST':
formA = AForm(request.POST)
formB = BForm(request.POST)
formB.save()
formA.save()
return HttpResponseRedirect('/log')
Here are my models:
# Descprition
class B(models.Model):
id = models.AutoField(primary_key=True)
description = models.CharField(max_length=50)
# Title
class A(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField('Name',max_length=20)
desc = models.ForeignKey(B)
and here is my form:
class BForm(forms.ModelForm):
class Meta:
model = B
fields = ['description']
class AForm(forms.ModelForm):
class Meta:
model = A
fields = ['name']
Your program has multiple errors but the main problem for this is because desc is a foreign key in class A that points to class B, and you don't have null=True on it, meaning you never want that field to be empty. In other words, each instance of A should have a foreign key desc.
If you just save() both forms, formA tries to save an instance of A, without having a value for desc field, hence the error. You should assign the instance that formB creates to the instance that formA creates:
new_b = formB.save()
new_a = formA.save(commit=False)
new_a.desc = new_b
new_a.save()
Other problems in your program including never called form.is_valid(), having redundant id fields(django would create one for you). I suggest you read django tutorial first before jumping into coding. It would save a lot of time like figuring out errors like this.
When creating a table query, I would like to modify my select statement by mapping the default table query. However, I cannot find a way to map the value of a column and still map to my case class
case class MyRecord(id: Int, name: String, value: Int)
class MyTable(tag: Tag) extends Table[MyRecord](tag, "MYTABLE") {
def id = column[Int]("id")
def name = column[String]("name")
def value = column[Int]("value")
def * = (id, name, value) <> (MyRecord.tupled, MyRecord.unapply)
}
lazy val tableQuery = TableQuery[MyTable]
I would like to trim the value of name with this function:
def trimLeading0: (Rep[String]) => Rep[String] = SimpleExpression.unary[String, String] {
(str, queryBuilder) =>
import slick.util.MacroSupport._
import queryBuilder._
b"TRIM(LEADING 0 FROM $str)"
}
Now I am at a loss about what to do here:
val trimmedTableQuery: Query[MyTable, MyRecord, Seq] = tableQuery.map(s => ???)
I have tried mapping the Rep like I would do with a case class:
val trimmedTableQuery = tableQuery.map(s => s.copy(name = trimLeading0(s.name)))
This refuses to compile with value copy is not a member of MyTable
My current workaround is to use a custom function instead of MyRecord.tupled for the default projection:
def trimming(t: (Int, String, Int)) = MyRecord(t._1, t._2.dropWhile(_ == "0"), t._3)
def * = (id, name, value) <> (trimming, MyRecord.unapply)
Alternatively, I could map the returned result of the DBIOAction returning a tuple to the case class, which is much less elegant:
val action = tableQuery.map{ s => (s.id, trimLeading0(s.name), s.value)}.result
val futureTuples: Future[Seq[(Int, String, Int)]] = db.run(action)
val records = futureTuples map (s => s.map(MyRecord.tupled))
But how can I do it inside the map method while building the query? OR would it be better to change the def name column description?
You can't mess with the default projection (i.e. def *) in MyTable as it needs to be symmetric. It's used for query and insert. But you can create a trimmedTableQuery based on a specialisation of MyTable with an overridden default projection. Then you can also have tableQuery based on the symmetric default projection. You will get an error if you try to do inserts based on the trimmedTableQuery (but you shouldn't need to do that, just use tableQuery for inserts).
lazy val tableQuery = TableQuery[MyTable]
lazy val trimmedTableQuery = new TableQuery(new MyTable(_) {
override def * = (id, trimLeading0(name), value) <> (MyRecord.tupled, MyRecord.unapply)
})
In SQLAlchemy, I want to define a mixin that automatically creates an Index in inheriting tables.
Assuming that the inheriting table has a member list called 'keys', I want the mixin to create in the inheriting table a single multi-column Index over the columns listed in keys. However, the mixin doesn't know what the keys are until the table is created!
How would I do this?
Example:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy import MetaData, Column, Index, Integer
metadata = MetaData()
Base = declarative_base(metadata=metadata)
class MyMixin(object):
#declared_attr
def __table_args__(cls):
return (Index('test_idx_%s' % cls.__tablename__, *cls.INDEX),)
class MyModel(MyMixin, Base):
__tablename__ = 'atable'
INDEX = ('a', 'b',)
id = Column(Integer, primary_key=True)
a = Column(Integer)
b = Column(Integer)
c = Column(Integer)
if __name__ == '__main__':
from sqlalchemy import create_engine
engine = create_engine('sqlite:///', echo=True)
metadata.bind = engine
metadata.create_all()
Docs: Combining Table/Mapper Arguments from Multiple Mixins