2009-07-26 8 views
32

Potrzebuję niektórych właściwości, żeby były wyjątkowe. Jak mogę to osiągnąć?Jak zdefiniować unikalną właściwość modelu w Google App Engine?

Czy jest coś takiego jak unique=True?

Używam Google App Engine do Pythona.

+0

Blixt, edycja, aby tytuł twierdził, że silnik aplikacji jest dobry, ponieważ wiele pytań dotyczących mechanizmu aplikacji jest agnostycznych, ale gdzieś na to pytanie należy wspomnieć, że plakat jest zainteresowany pythonową odpowiedzą, a nie java. –

+0

Masz rację. Zastanowiłem się też nad dodaniem go wcześniej, ale uznałem, że rozwiązanie Javy również wskaże OP w dobrym kierunku. Ale przypuszczam, że wersje GAE w języku Java i Python są na tyle różne, aby było to pytanie specyficzne dla Pythona. – Blixt

Odpowiedz

21

Nie ma wbudowanego ograniczenia, aby upewnić się, że wartość jest unikalna. Można to zrobić, jednakże:

query = MyModel.all(keys_only=True).filter('unique_property', value_to_be_used) 
entity = query.get() 
if entity: 
    raise Exception('unique_property must have a unique value!') 

używam keys_only=True ponieważ będzie to poprawić wydajność nieco przez nie pobierania danych dla jednostki.

Bardziej wydajną metodą byłoby użycie oddzielnego modelu bez pól, których nazwa klucza składa się z nazwy i wartości właściwości. Następnie można użyć get_by_key_name, aby pobrać jedną lub więcej nazw tych złożonych kluczy, a jeśli otrzymasz jedną lub więcej wartości nie-None, wiesz, że są zduplikowane wartości (i sprawdzanie, które wartości nie były None, będziesz wiedzieć, które nie były wyjątkowy.)


Jak onebyone wspomniano w komentarzach, tych podejść – przez ich dostać pierwsze, wprowadzone później natury – uruchomić problemy współbieżności ryzyka. Teoretycznie można utworzyć encję tuż po sprawdzeniu istniejącej wartości, a następnie kod po sprawdzeniu będzie nadal wykonywany, prowadząc do powielenia wartości. Aby temu zapobiec, trzeba będzie użyć transakcji: Transactions - Google App Engine


Jeśli szukasz, aby sprawdzić wyjątkowość całej wszystkie podmioty z transakcji, trzeba by umieścić je wszystkie w tej samej grupie stosującej pierwsza metoda, która byłaby bardzo nieefektywna. W przypadku transakcji, należy użyć drugiej metody tak:

class UniqueConstraint(db.Model): 
    @classmethod 
    def check(cls, model, **values): 
     # Create a pseudo-key for use as an entity group. 
     parent = db.Key.from_path(model.kind(), 'unique-values') 

     # Build a list of key names to test. 
     key_names = [] 
     for key in values: 
      key_names.append('%s:%s' % (key, values[key])) 

     def txn(): 
      result = cls.get_by_key_name(key_names, parent) 
      for test in result: 
       if test: return False 
      for key_name in key_names: 
       uc = cls(key_name=key_name, parent=parent) 
       uc.put() 
      return True 

     return db.run_in_transaction(txn) 

UniqueConstraint.check(...) zakłada, że ​​każda para pojedynczy klucz/wartość musi być unikalna, aby powrócić sukces. Transakcja będzie korzystać z pojedynczej grupy jednostek dla każdego typu modelu. W ten sposób transakcja jest niezawodna dla kilku różnych pól jednocześnie (dla tylko jednego pola byłoby to znacznie prostsze). Nawet, jeśli masz pola o tej samej nazwie w jednym lub większej liczbie modeli, nie będą one w konflikcie z wzajemnie.

+0

Możliwe jest umieszczenie ich w transakcjach, ale zostawiam to OP. – Blixt

+0

@onebyone: Zaktualizowałem swój post z kodem, aby pokazać, co miałem na myśli w przypadku drugiej metody. Używa bardzo szerokich grup encji, ale mam nadzieję, że nie robi więcej niż pięć wstawiania "put" na sekundę na model. – Blixt

+0

Czy dobrze rozumiem, że ten tworzy grupę encji dla każdej klasy, ograniczając prędkość wstawiania z prędkością jednej sekundy? Czy wprowadziłaby jakąkolwiek niespójność, aby sprawić, że wartość mieszająca wszystkich sprawdzanych właściwości zostanie dodana jako ciąg do "unikalnych wartości", tworząc w ten sposób wiele grup encji? – petr

24

Google ma funkcję pod warunkiem, aby to zrobić:

http://code.google.com/appengine/docs/python/datastore/modelclass.html#Model_get_or_insert

Model.get_or_insert(key_name, **kwds) 

próbach uzyskania podmiot o rodzaju modelki o podanej nazwie klucza. Jeśli istnieje, get_or_insert() po prostu go zwraca. Jeśli nie istnieje, tworzona jest, przechowywana i zwracana nowa encja o danym rodzaju, nazwie i parametrach w kwds.

Zdobądź i kolejne (możliwe) put są zawijane w transakcji w celu zapewnienia atomowości. Oznacza to, że funkcja get_or_insert() nigdy nie nadpisze istniejącej jednostki i wstawi nową jednostkę wtedy i tylko wtedy, gdy nie istnieje żadna jednostka o podanym rodzaju i nazwie.

Innymi słowy, get_or_insert() jest równoznaczne z tym kodzie Pythona:

def txn(): 
    entity = MyModel.get_by_key_name(key_name, parent=kwds.get('parent')) 
    if entity is None: 
    entity = MyModel(key_name=key_name, **kwds) 
    entity.put() 
    return entity 
return db.run_in_transaction(txn) 

argumentami:

KEY_NAME Nazwa klucza podmiotu ** kwds Złożone argumenty przekazać do konstruktora klasy modelu, jeśli instancja o podanej nazwie klucza nie istnieje. Argument macierzysty jest wymagany, jeśli pożądana jednostka ma element nadrzędny.

Uwaga: get_or_insert() nie przyjmuje obiektu RPC.

Metoda zwraca wystąpienie klasy modelu, która reprezentuje żądaną jednostkę, niezależnie od tego, czy istniała, czy została utworzona za pomocą metody. Podobnie jak w przypadku wszystkich operacji składowania danych, ta metoda może wywołać błąd TransactionFailedError, jeśli transakcji nie można ukończyć.