SQLAlchemy with Sqlite ON DELETE,UPDATE CASCADE not working - sqlite

I'm using sqlalchemy as ORM in flask with sqlite as DB for my web application.
I've 4 tables in my DB and I wanna implement ON DELETE CASCADE, ON UPDATE CASCADE correctly.
My relationships are :
Table Student and Subject has 1-to-many relationship with table Major
Table Mark is an association table between Table Subject and Student
so the expected behavior is :
When I delete,update a Student or Subject the correspending line gots deleted from table Mark(association table).
When I delete,update a Major the correspending line gots deleted from tables Student and Subject.
These are my classes :
class Student(db.Model, UserMixin):
__tablename__ = 'Student'
.......
major_id = db.Column(db.Integer, db.ForeignKey('Major.id'))
class Subject(db.Model, UserMixin):
__tablename__ = 'Subject'
.......
major_id = db.Column(db.Integer, db.ForeignKey('Major.id'))
class Mark(db.Model, UserMixin):
__tablename__ = 'Mark'
.....
stdnt_id = db.Column(db.Integer,db.ForeignKey('Student.regNumber'))
subj_id = db.Column(db.String(5),db.ForeignKey('Subject.code'))
.......
class Major(db.Model, UserMixin):
__tablename__ = 'Major'
............
students = db.relationship('Student', backref='major', lazy='dynamic')
subjects = db.relationship('Subject', backref='major', lazy='dynamic')
I've tried : onupdate="cascade",ondelete="cascade",cascade="all,delete, delete-orphan",passive_deletes=True............
and tested them in my web app but to no avail.
NB: I've checked my sqlite database file sometimes and found ON DELETE,UPDATE CASCASE constraints added to my db scheme but the cascade not working from my wep app.
Please any help ?

I tried and test many links and can't delete the parent. until I get one of the link that can delete the parent and automatically delete all its children. but can't update. here is the simplified code below,
class Parent(db.Model):
__tablename__ = 'parent'
id = db.Column(db.Integer, primary_key=True)
children = db.relationship(
"Child", back_populates="parent",
cascade="all, delete",
passive_deletes=True
)
class Child(db.Model):
__tablename__ = 'child'
id = db.Column(db.Integer, primary_key=True)
parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete="CASCADE"))
parent = db.relationship("Parent", back_populates="children")

Related

ON_DELETE="CASCADE" does not work with flask-sqlalchemy

I am developing a WebApp in flask and using flask-sqlalchemy to define my Models and store them in an SQLite database. In short, I have the following 3 models (A Porject can have multiple files, and each file can have multiple results):
class Project(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
files = db.relationship("File", backref="project", passive_deletes=True)
class File(db.Model):
id = db.Column(db.Integer, primary_key=True)
path = db.Column(db.String, nullable=False)
project_id = db.Column(
db.Integer,
db.ForeignKey("project.id", ondelete="CASCADE"),
nullable=False)
results = db.relationship("Result", backref="file", passive_deletes=True)
class Result(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String, nullable=False)
file_id = db.Column(
db.Integer,
db.ForeignKey("file.id", ondelete="CASCADE"),
nullable=False)
Now the problem is, that when I create a project, create and assign some files to the project, create some results and assign them to some files, and finally delete the project, the cascade deleting of the files and the results does not work. I found the following post and implemented the db.relationship attributes as suggested there, but the problem remains the same.
P.S.: here is a minimal working example, that shows the problem.
P.P.S.: Can somebody confirm, that the problem is reproducible. Not that it only happens on my computer/my environment.
Well, this answer goes in the category RTFM. As Gord Thompson "mentioned" with the link to the documentary of sqlalchemy, one needs to add the following lines somewhere reachable on flask app start:
from sqlalchemy.engine import Engine
from sqlalchemy import event
db = SQLAlchemy(app)
#event.listens_for(Engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record):
cursor = dbapi_connection.cursor()
cursor.execute("PRAGMA foreign_keys=ON")
cursor.close()
(now I am asking myself, why not a single word is written about this in the flask-sqlalchemy docu :-/ )

How to insert value in the table having it primary key in symfony

I want to insert a value having already defined id in the table which have auto increment column id(PK) in doctrine symfony.
You can change the ID generator temporarily to store given primary ids like:
use App\Entity\YourEntity;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Id\AssignedGenerator;
use Doctrine\ORM\Mapping\ClassMetadata;
$entity = new YourEntity();
$entity->setId(101);
$entity->setSomething('foo');
$entityManager->persist($entity);
// change the ID generator temporarily before flushing
$metadata = $entityManager->getClassMetaData(YourEntity::class);
$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);
$metadata->setIdGenerator(new AssignedGenerator());
$entityManager->flush();
Note : generally you should not update the id of an existing row in the dataBase.
But However.. Assuming that your entity look like has fields : ID and nameValue :
$car = $carsRepository->find(10); // select a car which the id is 10
$car->setId(1025); // note that : by default your entity DOES NOT have a built method setID() so you have to write it.
$car->setNameValue("Opel");
$manager->flush();

Symfony: queryBuilder without mapped field

it's possible to create a QueryBuilder in Symfony with fields that not appear in the Entity Class.
Something like this:
MyEntity Class:
id, name, color
DB Table: id, name, color, number
And then, I'm triying to do this:
$filterBuilder = $this->get('doctrine.orm.entity_manager')
->getRepository('XXXXBundle:MiEntity')
->createQueryBuilder('o')
->select('o.number')
->..........
;
But I get this error:
Error: Class XXXXX has no field or association named number
In your case, you should still add an unmapped "number" proporty to your class, but if you insist, you can use a native query to fetch whatever you want from the DB.
Here is an example that selects all users with an age > 20 :
$query = $em->createQuery('SELECT u FROM MyProject\Model\User u
WHERE u.age > 20');
$users = $query->getResult();
As seen on Doctrine Query Language documentation page

How to map OneToMany within a table?

o-o!
I am trying to map the wp_posts table from WordPress database. I have left out information to simplify my question:
This is the entity:
#Entity
#Table(name = "wp_posts")
#Getter
#Setter
public class WPPosts {
#Id
#Column(name = "ID")
private long id;
#Column(name = "post_parent")
private long postParent;
#OneToMany(targetEntity = WPPosts.class, mappedBy = "postParent")
List<WPPosts> childPosts;
}
An example for the table could be (again: simplified!):
id --- post_parent
1 ---- null
2 ---- 1
3 ---- 1
4 ---- null
So entity with id 1 should get a list childPosts of size 2 (containing post 2 and 3).
Unfortunately this does not work. I do not get any exception, but the list stays empty.
mappedBy is supposed to be referring to an entity, not an id. You need a #ManyToOne WPPosts postParent field for your mapping to work.
Note that with such a mapping, WPPosts.postParent is the owning side of the relationship, meaning that only changes to WPPosts.postParent will be honored by JPA; changes to WPPosts.childPosts will be ignored.
As far as accessing the list goes, you'll want to ensure to either only access the list within a transaction context, or declare the #OneToMany as fetch=FetchType.EAGER. Otherwise, you'll get lazy initialization exceptions.

How do I setup up Doctrine's Class Inheritance in Symfony?

My issue is, I'm having trouble grasping DiscriminatorColumn and DiscriminatorMap in Doctrine's Class Inheritance.
I have a products entity that is considered the parent class / table.
There are several child entities that inherit the product entity. (models, parts, and options)
I feel like I should be able to use the primary key to link both tables... But how do I do that with DiscriminatorColumn?
Here is the general idea of what I want to happen...
Fetch all model objects from database while inheriting product parent entity
SELECT object
FROM parts_object parts
LEFT JOIN products_object po
ON parts.product_fk = po.product_id
Or... Fetch all part objects from database while inheriting product parent entity
SELECT object
FROM parts_object parts
LEFT JOIN products_object po
ON parts.product_fk = po.product_id
Ideally I want this done using Doctrine instead of some custom SQL.
Do I need to setup a "type" column for the parent table so each row defines whether it's a part, model, or option?
Doctrine inheritance docs
Okay, I'll try to explain this as simple as possible.
Let's start with DiscriminatorColumn
Discriminator column is basically, as it says, a column in your database. Its used to store, a key, if you like which helps to identify what kind of object you're currently querying, based on your DiscriminatorMap configuration.
DiscriminatorMap is the way you map each of those keys to an entity. You said you have the following
Product [parent]
Model [child of parent]
Part [child of parent]
Option [child of parent]
Then, your discriminator map should look something like this, for example:
#DiscriminatorMap({
"model" = "AppBundle\Entity\Model",
"Part" = "AppBundle\Entity\Part",
"Option" = "AppBundle\Entity\Option"
})
Always pay attention to your last definition in your mapping. The last line must end without a comma!
As of InheritanceType I would suggest you to use #InheritanceType("JOINED") because this will let you have single table for each of your child classes.
Every child class must extend your Product entity class, which is obviously the parent. Each child class must not define $id property, because of the inheritance mapping.
Then querying for records by specific type comes with the following query:
"SELECT product FROM AppBundle\Entity\Product product WHERE product INSTANCE OF AppBundle\Entity\Part"
The query will search only for records mapped to this entity only.
If you have any questions, don't hesitate to ask.
Edit as of new comment
-----------------------
A little bit more explanation. You do not need to create any extra property/column in your entity mappings. The moment you add this annotation #DiscriminatorColumn(name="discr", type="string") doctrine will create that column automatically for you. The column from this example would be named discr with type of VARCHAR.
I still don't understand what is used to join the tables. How does doctrine know to link the ids between the product and model
About this part. If you use #InheritanceType("JOINED") this would mean that your GeneratedValue ID would be set in your main entity - Product. Then each of the child entities that extend Product would automatically get the same ID, which is why you don't need to specify $id property in your child entities.
Lastly, how can you check which entity type you're currently viewing for example. Consider the following scenario, each of your child entities extends Product and we will perform a dummy search for a record:
$product = $entityManager->find('AppBundle:Product', 1); // example
Now, if you actually go and do a var_dump($product) you will notice something interesting. The object would be an instance of either Model,Part or Option because each of these entities are defined in your discriminator map and Doctrine automatically maps your records based on that.
Later, this can come handy in situations like this:
if( $product instanceof \AppBundle\Entity\Part ) {
// do something only if that record belongs to part.
}
If you want to use DiscriminatorMap for Doctrine, so you should use Doctrine, but not SQL.
Basic setup is:
/**
* #ORM\Table(name="product")
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\ProductRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="productType", type="string")
* #ORM\DiscriminatorMap({
* "Product" = "Product",
* "Model" = "Model",
* "Part" = "Part",
* "Option" = "Option",
* })
*/
class Product
{
...
}
MyApp\ProductBundle\Entity\Model
/**
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\ModelRepository")
*/
class Model extends Product
{
}
MyApp\ProductBundle\Entity\Part
/**
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\PartRepository")
*/
class Part extends Product
{
}
MyApp\ProductBundle\Entity\Option
/**
* #ORM\Entity(repositoryClass="MyApp\ProductBundle\Repository\OptionRepository")
*/
class Option extends Product
{
}
Then if you need to get all products at controller
$em = $this->getDoctrine()->getManager();
$repo = $em->getRepository("MyAppProductBundle:Product");
$products = $repo->findAll();
Then if you need select all models, just setup proper repository
$repo = $em->getRepository("MyAppProductBundle:Model");
$models = $repo->findAll();

Resources