سؤال

Python noob here,

Currently I'm working with SQLAlchemy, and I have this:

from __init__ import Base
from sqlalchemy.schema import Column, ForeignKey
from sqlalchemy.types import Integer, String
from sqlalchemy.orm import relationship

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True)
    username = Column(String, unique=True)
    email = Column(String)
    password = Column(String)
    salt = Column(String)
    openids = relationship("OpenID", backref="users")

User.__table__.create(checkfirst=True)

#snip definition of OpenID class

def create(**kwargs):
    user = User()
    if "username" in kwargs.keys():
        user.username = kwargs['username']
    if "email" in kwargs.keys():
        user.username = kwargs['email']
    if "password" in kwargs.keys():
        user.password = kwargs['password']

    return user

This is in /db/users.py, so it would be used like:

from db import users
new_user = users.create(username="Carson", password="1234")
new_user.email = "email@address.com"
users.add(new_user) #this function obviously not defined yet

but the code in create() is a little stupid, and I'm wondering if there's a better way to do it that doesn't require an if ladder, and that will fail if any keys are added that aren't in the User object already. Like:

for attribute in kwargs.keys():
    if attribute in User:
        setattr(user, attribute, kwargs[attribute])
    else:
        raise Exception("blah")

that way I could put this in its own function (unless one hopefully already exists?) So I wouldn't have to do the if ladder again and again, and so I could change the table structure without modifying this code.

Any suggestions?

هل كانت مفيدة؟

المحلول

My suggestion would be to not simplify it any further. You risk stepping on important object structures if you assign arbitrary attributes.

The one simplification I would do is to drop .keys() when you use it on a dict; both containment checking and iteration already use the keys.

...

On second thought, you could have a class attribute that contains known safe attributes, and then check this attribute within the function, and use setattr() on the instance.

نصائح أخرى

Actually the declarative base class already inserts the exact constructor that you are looking for, as documented in the declarative modules docs. So just doing User(username="Carson", password="1234") will do what you want and User(something_not_an_attribute='foo') will raise an exception.

If you don't need to cover inherited attributes,

def create(**kwargs):
    keys_ok = set(User.__dict__)
    user = User()
    for k in kwargs:
        if k in keys_ok:
            setattr(user, k, kwargs[k])

If you do need to cover inherited attributes, inspect.getmembers can help (with a custom predicate to avoid members whose names start with underscore, or others you want to ensure can't be set this way).

I would also consider (at least) giving a warning if set(kwargs) - set(keys_ok) is not empty -- i.e., if some of the named arguments passed to create cannot be set as arguments in the created instance; that can't be a good thing...!-)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top