2009-06-29 14 views
16

Mam aplikację Django, która pobiera dane całkowicie z zewnętrznego źródła (zapytanie przez HTTP). Oznacza to, że nie mam opcji lokalnej bazy danych. Dane sesji są przechowywane w pamięci podręcznej (na moim serwerze programistycznym używam bazy danych SQLite, więc nie jest to źródło błędów). Używam krwawiącego Django 1.1svn.Użytkownicy i autoryzacja Django z zewnętrznego źródła

Wprowadź problem: chcę używać systemu uwierzytelniania Django dla użytkowników.

Wydaje się dość prosty napisać własne zaplecze uwierzytelniające, ale zawsze pod warunkiem, że masz lokalną bazę danych, w której można zapisać użytkowników. Bez bazy danych moim głównym problemem jest wytrwałość.

Próbowałem ją następująco (zakładamy, że datasource.get() to funkcja zwraca jakąś dict):

class ModelBackend (object): 
    """Login backend.""" 

    def authenticate (self, username=None, password=None): 
     """Check, if a given user/password combination is valid""" 

     data = datasource.get ('login', username, password) 
     if data and data['ok']: 
      return MyUser (username=username) 
     else: 
      raise TypeError 
      return None 

    def get_user (self, username): 
     """get data about a specific user""" 

     try: 
      data = datasource.get ('userdata', username) 
      if data and data['ok']: 
       return data.user 
     except: 
      pass 
     return None 


class MyUser (User): 
    """Django user who isn't saved in DB""" 

    def save (self): 
     return None 

Ale celowo brakuje save() metoda na myuser wydaje się przełamać pamięci sesji o logowaniu.

Jak powinien wyglądać MyUser bez lokalnej bazy danych?

+4

Właśnie znalazłem Wydaje się robić to, co chcę. Jeśli ją ocenię, opublikuję odpowiedź (nie zniechęcaj się do dodawania własnych ;-)). – Boldewyn

+0

Łącze ma nieprzemyślane końcowe ">" ... cekiny. – Boldewyn

Odpowiedz

22

OK, jest to o wiele bardziej skomplikowane, niż myślałem. Najpierw zacznij od http://docs.djangoproject.com/en/dev/howto/auth-remote-user/, ale musisz rozszerzyć go o własne zaplecze i użytkownika.

from django.contrib.auth.backends import RemoteUserBackend 

class MyRemoteUserBackend (RemoteUserBackend): 
    # Create a User object if not already in the database? 
    create_unknown_user = False 

    def get_user (self, user_id): 
     user = somehow_create_an_instance_of (MyUser, user_id) 
     return user 

    def authenticate (self, **credentials): 
     check_credentials() 
     user = somehow_create_an_instance_of (MyUser, credentials) 
     return user 

Następnie użytkownik:

from django.contrib.auth.models import User 

class MyUser (User): 

    def save (self): 
     """saving to DB disabled""" 
     pass 

    objects = None # we cannot really use this w/o local DB 

    username = "" # and all the other properties likewise. 
        # They're defined as model.CharField or similar, 
        # and we can't allow that 

    def get_group_permissions (self): 
     """If you don't make your own permissions module, 
      the default also will use the DB. Throw it away""" 
     return [] # likewise with the other permission defs 

    def get_and_delete_messages (self): 
     """Messages are stored in the DB. Darn!""" 
     return [] 

Uff! Django naprawdę nie jest przeznaczony do użytku bez bazy danych ...

+1

co trzeba było zmienić w pliku ustawień? Próbuję to zrobić i to tak, jakby całkowicie ignorował mój zdalny backend. – Colleen

+1

Ustawiłeś coś wzdłuż 'AUTHENTICATION_BACKENDS = ( 'myapp.MyappUserBackend', )'? – Boldewyn

+0

Tak.Rozgryzłem to, potrzebowałem rozszerzyć zdalne oprogramowanie pośrednie w moim przypadku, ponieważ przekazywałem dane uwierzytelniające inaczej niż domyślne. – Colleen

1

grep ping źródłem wykazały, że jedynym miejscem user.save() jest faktycznie nazywa (z wyjątkiem tworzenia użytkownika i kod zarządzania hasłami, który nie trzeba używać w ogóle) jest django.contrib.auth.login(), aby zaktualizować user.last_login wartość.

# TODO: It would be nice to support different login methods, like signed cookies. 
user.last_login = datetime.datetime.now() 
user.save() 

Jeśli nie chcesz, dane użytkownika, aby odpocząć w DB, spróbuj dodać obojętne save() metody. Jeśli mam rację, powinno działać.

def save(self, *args, **kwargs): 
    pass 

Oczywiście, ponieważ nie masz wytrwałości w ogóle, należy rozważyć buforowanie datasource.get wyników, poza tym najgorszym wypadku może skończyć się zapytań dane ponownie i ponownie na każdy zalogowany przeboju użytkownika.

4

Zamiast nadpisywać metodę save, można również odłączyć sygnał, który ją wywołuje. To właśnie robię w niektórych aplikacjach, które mają dostęp tylko do odczytu do bazy danych użytkowników.

# models.py (for instance) 
from django.contrib.auth.models import update_last_login, user_logged_in 
user_logged_in.disconnect(update_last_login) 
+0

Pracuję z Django 1.6 i OP + to uratowało mój dzień! Dziękuję Ci! – enpenax