2017-07-12 1 views
0

Ich baue eine Postgressql-Datenbank mit SQLALCHEMY. Ich frage mich, ob es möglich ist, eine Struktur wie folgt zu haben:SQL-Feld mit einer fremden ID aus zwei Tabellen

Zwei Tabellen, bestehend aus Personen und Organisationen.

Table - Person - 
person_id, name 
1,   Jeffery 
2,   Frank 

Table - Org - 
org_id, name 
a,  Pepsi 
b,  Microsoft 

Eine dritte Tabelle von Adressen oder entweder Personen oder Organisationen:

Table - Addresses - 
address_id, type_of_id, (either person_id or org_id), addresses 
1,   person,  1, "2 Jeffery way" 
2,   person,  1, "7 Holland Avenue" 
3,   org,  b, "1 Microsoft way" 
4,   person,  2, "2 Frank Street" 

Ist dies möglich mit Postgres? Und wie schreibe ich es in sqlalchemy Python-Code?

Update:

Ich glaube, dass dies eine polymorphe Vereinigung wie in genannt wird: Possible to do a MySQL foreign key to one of two possible tables?

+0

Dies ist tatsächlich möglich, obwohl, aus einer * relationalen * Sichtweise, nicht viel Sinn macht. Normalerweise hätten die Tabellen "Person" und "Org" eine Spalte "address_id", die auf "Addresses (address_id)" verweist und die Adressen seien Adressen (keine person org oder person_id dort). – joanolo

+0

Was passiert, wenn ich mehrere Adressen pro Person und mehrere Adressen für Organisationen habe? – labjunky

+0

Ich würde für zwei "Link-Tabellen" gehen: eine für 'person_x_address (person_id, address_id)', und eine andere für 'organization_x_address (organisation_id, address_id) '. Es wird * sauberer * als die meisten anderen Optionen. – joanolo

Antwort

0

Ihre Frage aus zwei Teilen besteht:

  1. eine Vereinigung der Personen und Organisationen erstellen Tabelle .
  2. Fügen Sie der Tabelle den Typ der Entität (Person/Organisation) hinzu.

Der erste Teil kann mit union_all durchgeführt werden. Weitere Informationen hierzu finden Sie unter:

zweiten Teil, die Art der Einheit Hinzufügen (Person/Organisation) kann mit literal_column erreicht werden. Weitere Informationen zu diesem Thema:

Hier finden Sie ein kurzes Beispiel die Kombination dieser Elemente:

from sqlalchemy import Column, Integer, String 
from sqlalchemy.ext.declarative import declarative_base 
from sqlalchemy import create_engine 
from sqlalchemy.orm import sessionmaker 

Base = declarative_base() 


## Define sample models 


class Person(Base): 
    __tablename__ = 'person' 
    person_id = Column('id', Integer, primary_key=True) 
    name = Column('name', String(250)) 

    def __init__(self, name): 
     self.name = name 

    def __repr__(self): 
     return '[{person_id}] - {name}'.format(person_id=self.person_id, name=self.name) 


class Orqanisation(Base): 
    __tablename__ = 'organisation' 
    organisation_id = Column('id', Integer, primary_key=True) 
    name = Column('name', String(250)) 

    def __init__(self, name): 
     self.name = name 

    def __repr__(self): 
     return '[{organisation_id}] - {name}'.format(organisation_id=self.organisation_id, name=self.name) 


engine = create_engine('sqlite:///') 
session = sessionmaker() 
session.configure(bind=engine) 
ex_ses = session() 
Base.metadata.create_all(engine) 

## Create sample data 

# http://listofrandomnames.com/ for something else then A,B,... 
names = ['Virgil', 'Ina', 'Oleta', 'Suzette', 'Usha', 'Ilda', 'Lorean', 'Cinthia', 'Sheba', 'Waneta', 'Donnette'] 

organisations = ['stackoverflow','Cross validated','Meta stack overflow','Area 51'] 

# Create persons 
for name in names: 
    ex_ses.add(Person(name=name)) 

# Create organisations 
for org in organisations: 
    ex_ses.add(Orqanisation(name=org)) 

ex_ses.commit() 

# queries 
print('Persons:') 
for person in ex_ses.query(Person).all(): 
    print('* ',person) 

''' 
Persons: 
* [1] - Virgil 
* [2] - Ina 
* [3] - Oleta 
* [4] - Suzette 
* [5] - Usha 
* [6] - Ilda 
* [7] - Lorean 
* [8] - Cinthia 
* [9] - Sheba 
* [10] - Waneta 
* [11] - Donnette 
''' 

print('Organisations:') 
for org in ex_ses.query(Orqanisation).all(): 
    print('* ',org) 

''' 
Organisations: 
* [1] - stackoverflow 
* [2] - Cross validated 
* [3] - Meta stack overflow 
* [4] - Area 51 
''' 

# Query combining tables, adding type of entity 

from sqlalchemy.sql.expression import literal_column 

persons = ex_ses.query(Person.person_id, Person.name,literal_column("'person'").label('type')) 
organisations = ex_ses.query(Orqanisation.organisation_id, Orqanisation.name,literal_column("'Organisation'").label('type')) 

print('Persons - Organisations:') 
for pers_org in persons.union_all(organisations).all(): 
    print('* {id} - {name} (type: {type})'.format(id=pers_org[0],name=pers_org[1],type=pers_org[2])) 

''' 
Persons - Organisations: 
* 1 - Virgil (type: person) 
* 2 - Ina (type: person) 
* 3 - Oleta (type: person) 
* 4 - Suzette (type: person) 
* 5 - Usha (type: person) 
* 6 - Ilda (type: person) 
* 7 - Lorean (type: person) 
* 8 - Cinthia (type: person) 
* 9 - Sheba (type: person) 
* 10 - Waneta (type: person) 
* 11 - Donnette (type: person) 
* 1 - stackoverflow (type: Organisation) 
* 2 - Cross validated (type: Organisation) 
* 3 - Meta stack overflow (type: Organisation) 
* 4 - Area 51 (type: Organisation) 
''' 
0

Adapting von: http://www.duanqu.tech/questions/2898814/sqlalchemy-polymorphic-association und http://docs.sqlalchemy.org/en/latest/orm/inheritance.html

from sqlalchemy.ext.declarative import as_declarative, declared_attr 
from sqlalchemy import create_engine, Integer, Column, \ 
    String, ForeignKey 
from sqlalchemy.orm import Session, relationship 
from sqlalchemy_utils import database_exists, create_database 
from sqlalchemy.dialects.postgresql import UUID 
import uuid 


@as_declarative() 
class Base(object): 
    # Set the tablenames 
    # to the class names 
    @declared_attr 
    def __tablename__(cls): 
     return cls.__name__.lower() 
    # give an id to each class 
    id = Column(UUID, default=lambda: str(uuid.uuid4()), primary_key=True) 
# Base = declarative_base() 


class EntityInterface(Base): 
    discriminator = Column(String) 

    __mapper_args__ = {"polymorphic_on": discriminator} 


class Address(Base): 
    street = Column(String) 
    city = Column(String) 
    zip = Column(String) 
    entity_id = Column(UUID, ForeignKey(EntityInterface.id), 
         default=lambda: str(uuid.uuid4())) 
    entity = relationship(EntityInterface) 

    def __repr__(self): 
     return ("%s(street=%r, city=%r, zip=%r, company=%r)" % 
       (self.__class__.__name__, self.street, self.city, 
       self.zip, self.entity)) 


class Person(EntityInterface): 
    id = Column(UUID, ForeignKey(EntityInterface.id), 
       default=lambda: str(uuid.uuid4()), primary_key=True) 
    name = Column(String) 
    __mapper_args__ = {"polymorphic_identity": "Person"} 


class Organization(EntityInterface): 
    id = Column(UUID, ForeignKey(EntityInterface.id), 
       default=lambda: str(uuid.uuid4()), primary_key=True) 
    name = Column(String) 
    __mapper_args__ = {"polymorphic_identity": "Organization"} 


engine = create_engine(
    'postgresql://paul_tsc:[email protected]/' + "poly_testing", 
    echo=False) 

# if it doesn't exist, create it 
if not database_exists(engine.url): 
    create_database(engine.url) 

Base.metadata.create_all(engine) 
session = Session(engine) 

address1 = Address(street='test-1', city="Detroit", zip="56785") 
address2 = Address(street='test-2', city="Phoenix", zip="110322") 
address3 = Address(street='test-3', city="Washington", zip="432414") 

org1 = Organization(name="Org-1 TEST") 
org2 = Organization(name="Org-2 TEST") 
person1 = Person(name="Person-1 TEST") 
person2 = Person(name="Person-2 TEST") 

address1.entity = org1 
address2.entity = person1 
address3.entity = person1 

session.add_all([address1, address2, address3]) 

session.commit() 

address3.entity = org2 

session.commit() 

print("PRINTING, TOTAL = %s" % session.query(Address).count()) 
for address in session.query(Address): 
    print("ADDRESS = %s" % address) 
1

Ich sehe zwei Möglichkeiten, dies in einer relationalen Datenbank zu modellieren:

  1. Beziehungstabelle

    CREATE TABLE address_assignment (
        person_id integer REFERENCES person, 
        org_id text REFERENCES org, 
        address_id integer NOT NULL REFERENCES address, 
        CHECK (person_id IS  NULL AND org_id IS NOT NULL 
         OR person_id IS NOT NULL AND org_id IS  NULL), 
        UNIQUE (person_id, org_id, address_id) 
    ); 
    

    Dieses Modell besonders nützlich sein würde, wenn verschiedene Einheiten eine Adresse teilen können.

  2. “ armen Mannes Super ”

    Erstellen Sie eine Tabelle, die eine übergeordnete Klasse für Mensch und Organisation implementiert, vielleicht alle gängigen Attribute enthalten:

    CREATE TABLE located_entity (
        locent_id INTEGER PRIMARY KEY, 
        name text NOT NULL 
    ); 
    

    Sowohl person und org einen Fremdschlüssel bekommen located_entity, und Sie können die Beziehung mit address implementieren, indem Sie auch einen Fremdschlüssel zu located_entity zuhinzufügen.

    Wenn Sie in die Regel Personen, die durch ihre Adresse nachschlagen, anstatt Adressen von Personen, könnten Sie in der anderen Richtung, mit einer ähnlichen Check-Bedingung wie in Lösung 1.

  3. Zugabe des Fremdschlüssels zwischen located_entity und person oder org Person betrachten
+0

Ich habe Option 2 oben implementiert – labjunky

Verwandte Themen