On Mon, Nov 6, 2017 at 12:47 PM, Sven <[email protected]> wrote:
> Hello,
>
> I'm actually working on an online-game which is already advanced and pretty
> consequent. As I explained in a previous mail, my goal is to replace the
> save system actually based on Pickle by SQLAlchemy. If it is possible, it
> would be better for me to stay with the initial classes organizations and
> try to map it to the Database without changing anything.
>
> It is a MUD, a textual game. Each player is in a room and each room contains
> exits leading to other rooms. For example :
>
> Player Brian is in a room "kitchen". The room "kitchen" contains an exit
> "north" leading to the room "bathroom". If player Brian type "north", he
> leaves the kitchen and goes in the bathroom.
>
> Here is my classes organization :
>
> Each room contains an instance of the class "Exits". Exits is a class
> container which contains an OrderedDict always based on the same template :
>
> EXIT_NAMES = OrderedDict()
> EXIT_NAMES["south"] = None
> EXIT_NAMES["southwest"] = None
> EXIT_NAMES["west"] = None
> EXIT_NAMES["northwest"] = None
> EXIT_NAMES["north"] = None
> EXIT_NAMES["northeast"] = None
> EXIT_NAMES["east"] = None
> EXIT_NAMES["southeast"] = None

OK when you have a table like this:

class Exit(Base):

            __tablename__ = "exit"
            id = Column(Integer, primary_key=True)
            id_sorties = Column(Integer,
ForeignKey("exits_container.id"), nullable = False)

            direction = Column(String)

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

            def __repr__(self):
                        return self.direction

that means if you are storing an "exit", you need to have a row with
an id and a direction.

then when you make a mapping like this:


class Exits(Base):

            __tablename__ = "exits_container"
            id = Column(Integer, primary_key=True)
            exits = relationship("Exit", collection_class=[anything at
all, your OrderedExits, or anything else])

Exits.exits is now a proxy directly to rows in the "exit" table.

So far so good.

This means that every element of data in Exits.exits links to a real
row in the table.

Which means you can't put "None" into that collection with a key like
"South".    Putting "South" in there as a key means,
some_exists.exits.append(Exit(direction="South")).   it means a row in
the table.    It doesn't make sense that you want to put information
in the dictionary but not define any way of persisting it.

There are three ways to work around this:

1. don't put "key": None into the dictionary at all.  Why does the key
need to be present with None?   You can alter the OrderedExists
collection class to return None for those keys, just without them
being persisted.  It looks like those directions are global / class
level in any case, you don't even need to be using an OrderedDict here
anyway if you just have those fixed keys (is it valid if I said
exists["FOOBAR"] = Exit("FOOBAR") ? can I make up new directions on
the fly?)


2. Store an Exit() object anyway.  Put a flag on it called "present",
start out with the flag being false, meaning it's like "None".

3. store the exits in some other collection that is somehow persisted
when the objects are present.    Too complicated, just do #1 or #2.
If these keys are truly present in all cases I think #2 is the most
correct.


>
> Initially, everything is setted by None. The room has no exit. When an exit
> is added, for example at the north of the room, EXIT_NAMES["north"] contains
> an instance of the class "Exit".
>
> Only the values different from None have to be inserted in the Database.
>
> I found the following example about the mapping of the OrderedDict in the
> SQLAlchemy documentation :
>
> http://docs.sqlalchemy.org/en/latest/orm/collections.html#custom-dictionary-based-collections
>
> I tried to use it and here is what I have done so far :
>
> from collections import OrderedDict
> from sqlalchemy import create_engine
> from sqlalchemy.orm import sessionmaker
> from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta
> from sqlalchemy import orm
> from sqlalchemy.orm import relationship
> from sqlalchemy import Column, Integer, Boolean, String, ForeignKey
> from sqlalchemy.orm.collections import attribute_mapped_collection
> from sqlalchemy.orm.collections import column_mapped_collection
> from sqlalchemy.util import OrderedDict
> from sqlalchemy.orm.collections import MappedCollection, collection
>
> Base = declarative_base()
>
> EXIT_NAMES = OrderedDict()
> EXIT_NAMES["south"] = None
> EXIT_NAMES["southwest"] = None
> EXIT_NAMES["west"] = None
> EXIT_NAMES["northwest"] = None
> EXIT_NAMES["north"] = None
> EXIT_NAMES["northeast"] = None
> EXIT_NAMES["east"] = None
> EXIT_NAMES["southheast"] = None
>
> class OrderedExits(OrderedDict, MappedCollection):
>
>       def __init__(self, *args, **kw):
>             MappedCollection.__init__(self, keyfunc=lambda node: node.name)
>             OrderedDict.__init__(self, *args, **kw)
>
> class Exits(Base):
>
>             __tablename__ = "exits_container"
>             id = Column(Integer, primary_key=True)
>             exits = relationship("Exit", collection_class=OrderedExits)
>
>             def __init__(self):
>                         self.exits = OrderedExits(EXIT_NAMES)
>
>             def __repr__(self):
>                         text = ""
>                         for s in self.exits.keys():
>                                     text = text + s + " : " +
> str(self.exits[s]) + "\n"
>                         return text
>
> class Exit(Base):
>
>             __tablename__ = "exit"
>             id = Column(Integer, primary_key=True)
>             id_sorties = Column(Integer, ForeignKey("exits_container.id"),
> nullable = False)
>
>             direction = Column(String)
>
>             def __init__(self, direction):
>                         self.direction = direction
>
>             def __repr__(self):
>                         return self.direction
>
> if __name__ == '__main__':
>
>             engine =
> create_engine('postgresql://USERNAME:PASSWORD@localhost/DATABASE', echo =
> True)
>
>             Base.metadata.create_all(engine)
>
>             Session = sessionmaker(bind=engine)
>             session = Session()
>
>             south = Exit("south")
>             west = Exit("west")
>
>             e = Exits()
>
>             e.exits["south"] = south
>             e.exits["west"] = west
>
>             session.add(south)
>             session.add(west)
>             session.add(e)
>
>             session.commit()
>
>             print(e)
>
>
> Here is the error that I get :
>
> Traceback (most recent call last):
>   File "C:\Users\Sven\Desktop\SQL Alchemy Tests\ordereddict.py", line 72, in
> <module>
>     e = Exits()
>   File "<string>", line 4, in __init__
>   File "C:\Python34\lib\site-packages\sqlalchemy\orm\state.py", line 415, in
> _initialize_instance
>     manager.dispatch.init_failure(self, args, kwargs)
>   File "C:\Python34\lib\site-packages\sqlalchemy\util\langhelpers.py", line
> 66, in __exit__
>     compat.reraise(exc_type, exc_value, exc_tb)
>   File "C:\Python34\lib\site-packages\sqlalchemy\util\compat.py", line 187,
> in reraise
>     raise value
>   File "C:\Python34\lib\site-packages\sqlalchemy\orm\state.py", line 412, in
> _initialize_instance
>     return manager.original_init(*mixed[1:], **kwargs)
>   File "C:\Users\Sven\Desktop\SQL Alchemy Tests\ordereddict.py", line 38, in
> __init__
>     self.exits = OrderedExits(EXIT_NAMES)
>   File "C:\Python34\lib\site-packages\sqlalchemy\orm\attributes.py", line
> 229, in __set__
>     instance_dict(instance), value, None)
>   File "C:\Python34\lib\site-packages\sqlalchemy\orm\attributes.py", line
> 1082, in set
>     new_values = list(iterable)
>   File "C:\Python34\lib\site-packages\sqlalchemy\orm\collections.py", line
> 1535, in _convert
>     new_key = self.keyfunc(value)
>   File "C:\Users\Sven\Desktop\SQL Alchemy Tests\ordereddict.py", line 28, in
> <lambda>
>     MappedCollection.__init__(self, keyfunc=lambda node: node.name)
> AttributeError: 'NoneType' object has no attribute 'name'
>
> SQLAlchemy doesn't know what to do with the None values, which is logical.
> How can I say to SQLAlchemy to ignore these values and how can I be sure
> that SQLAlchemy will be able to populate my objects and set the exits and
> the None values at the loading of the program ? What is the good way to
> proceed ?
>
> Thank you !
>
> Regards,
>
> Sven
>
> --
> SQLAlchemy -
> The Python SQL Toolkit and Object Relational Mapper
>
> http://www.sqlalchemy.org/
>
> To post example code, please provide an MCVE: Minimal, Complete, and
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full
> description.
> ---
> You received this message because you are subscribed to the Google Groups
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> To post to this group, send email to [email protected].
> Visit this group at https://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/d/optout.

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to