Storage ======= Different frameworks support different ORMs, Storage solves the different interfaces moving the common API to mixins classes. These mixins are used on apps when defining the different models used by ``python-social-auth``. Social User ----------- This model associates a social account data with a user in the system, it contains the provider name, the user ID (``uid``) which identifies the social account in the remote provider, and ``id_key`` naming the provider field from which that ID was obtained. It also contains JSON-encoded ``extra_data`` with additional provider information. Existing rows created before ``id_key`` was introduced use an empty string until social-core migrates them. When implementing this model, it must inherits from UserMixin_ and extend the needed methods: * Username:: @classmethod def get_username(cls, user): """Return the username for given user""" raise NotImplementedError('Implement in subclass') @classmethod def username_max_length(cls): """Return the max length for username""" raise NotImplementedError('Implement in subclass') * User model:: @classmethod def user_model(cls): """Return the user model""" raise NotImplementedError('Implement in subclass') @classmethod def changed(cls, user): """The given user instance is ready to be saved""" raise NotImplementedError('Implement in subclass') @classmethod def user_exists(cls, username): """ Return True/False if a User instance exists with the given arguments. Arguments are directly passed to filter() manager method. """ raise NotImplementedError('Implement in subclass') @classmethod def create_user(cls, username, email=None): """Create a user with given username and (optional) email""" raise NotImplementedError('Implement in subclass') @classmethod def get_user(cls, pk): """Return user instance for given id""" raise NotImplementedError('Implement in subclass') * Social user:: @classmethod def get_social_auth(cls, provider, uid, id_key=None): """Return UserSocialAuth for the provider, uid, and optional key""" raise NotImplementedError('Implement in subclass') @classmethod def get_social_auth_by_extra_data(cls, provider, key, value, id_key=''): """Return one unambiguous association matching stable provider data""" raise NotImplementedError('Implement in subclass') @classmethod def get_social_auth_for_user(cls, user): """Return all the UserSocialAuth instances for given user""" raise NotImplementedError('Implement in subclass') @classmethod def create_social_auth(cls, user, uid, provider, id_key=''): """Create a UserSocialAuth instance for given user""" raise NotImplementedError('Implement in subclass') @classmethod def migrate_social_auth(cls, social, uid, id_key): """Atomically replace the association identifier and its key""" raise NotImplementedError('Implement in subclass') Identifier migration must preserve the storage's provider/UID uniqueness guarantee, lock the association while updating it, and fail rather than select an arbitrary row when stored provider data matches multiple associations. * Social disconnection:: @classmethod def allowed_to_disconnect(cls, user, backend_name, association_id=None): """Return if it's safe to disconnect the social account for the given user""" raise NotImplementedError('Implement in subclass') @classmethod def disconnect(cls, name, user, association_id=None): """Disconnect the social account for the given user""" raise NotImplementedError('Implement in subclass') Nonce ----- This is a helper class for OpenID mechanism, it stores a one-use number, shouldn't be used by the project since it's for internal use only. When implementing this model, it must inherit from NonceMixin_, and override the needed methods:: @classmethod def use(cls, server_url, timestamp, salt): """Create a Nonce instance""" raise NotImplementedError('Implement in subclass') @classmethod def get(cls, server_url, salt): """Retrieve a Nonce instance""" raise NotImplementedError('Implement in subclass') @classmethod def delete(cls, nonce): """Delete a Nonce instance""" raise NotImplementedError('Implement in subclass') Association ----------- Another OpenID helper class, it stores basic data to keep the OpenID association. Like Nonce_ this is for internal use only. When implementing this model, it must inherits from AssociationMixin_, and override the needed methods:: @classmethod def store(cls, server_url, association): """Create an Association instance""" raise NotImplementedError('Implement in subclass') @classmethod def get(cls, *args, **kwargs): """Get an Association instance""" raise NotImplementedError('Implement in subclass') @classmethod def remove(cls, ids_to_delete): """Remove an Association instance""" raise NotImplementedError('Implement in subclass') Validation code --------------- This class is used to keep track of email validations codes following the usual email validation mechanism of sending an email to the user with a unique code. This model is used by the partial pipeline ``social_core.pipeline.mail.mail_validation``. Check the docs at *Email validation* in `pipeline docs`_. The model must inherit from ``CodeMixin`` and persist the ``email``, ``code``, ``verified``, and ``timestamp`` attributes. ``make_code()`` initializes ``timestamp`` with the creation time in UTC. A framework-managed creation timestamp can also be used. Naive timestamps are interpreted as UTC by the default ``is_expired(seconds)`` implementation; override this method if your storage uses another timezone. The Django integration interprets naive timestamps in Django's configured timezone. Codes without a timestamp are rejected when expiry is enabled. Existing storage integrations must add timestamp persistence and migrate existing data, or have users request new codes. Do not assign a new creation time to old codes. The storage lookup method must also be overridden:: @classmethod def get_code(cls, code): """Return the Code instance with the given code value""" raise NotImplementedError('Implement in subclass') Storage interface ----------------- There's a helper class used by strategies to hide the real models names under a common API, an instance of this class is used by strategies to access the storage modules. When implementing this class it must inherits from BaseStorage_, add the needed models references and implement the needed method:: class StorageImplementation(BaseStorage): user = UserModel nonce = NonceModel association = AssociationModel code = CodeModel @classmethod def is_integrity_error(cls, exception): """Check if given exception flags an integrity error in the DB""" raise NotImplementedError('Implement in subclass') SQLAlchemy and Django mixins ---------------------------- Currently there are partial implementations of mixins for `SQLAlchemy ORM`_ and `Django ORM`_ with common code used later on current implemented applications. .. note:: When using `SQLAlchemy ORM`_ and ``ZopeTransactionExtension``, it's recommended to use the transaction_ application to handle them. Models Examples --------------- Check for current implementations for `Django App`_, `Flask App`_, `Pyramid App`_, and `Webpy App`_ for examples of implementations. .. _UserMixin: https://github.com/python-social-auth/social-core/blob/master/social_core/storage.py#L21 .. _NonceMixin: https://github.com/python-social-auth/social-core/blob/master/social_core/storage.py#L166 .. _AssociationMixin: https://github.com/python-social-auth/social-core/blob/master/social_core/storage.py#L178 .. _BaseStorage: https://github.com/python-social-auth/social-core/blob/master/social_core/storage.py#L248 .. _SQLAlchemy ORM: https://github.com/python-social-auth/social-storage-sqlalchemy/blob/master/social_sqlalchemy/storage.py .. _Django ORM: https://github.com/python-social-auth/social-app-django/blob/master/social_django/storage.py .. _Django App: https://github.com/python-social-auth/social-app-django/blob/master/social_django/models.py .. _Flask App: https://github.com/python-social-auth/social-app-flask/blob/master/social_flask/models.py .. _Pyramid App: https://github.com/python-social-auth/social-app-pyramid/blob/master/social_pyramid/models.py .. _Webpy App: https://github.com/python-social-auth/social-app-webpy/blob/master/social_webpy/models.py .. _pipeline docs: pipeline.html#email-validation .. _transaction: https://pypi.python.org/pypi/transaction