31 Stimmen

Eindeutiges Modellfeld in Django und Groß-/Kleinschreibung (Postgres)

Stellen Sie sich die folgende Situation vor: -

Angenommen, meine Anwendung ermöglicht es Benutzern, die Staaten / Provinzen in ihren Land zu erstellen. Nur zur Klarstellung: Wir betrachten hier nur ASCII-Zeichen hier.

In den USA könnte ein Benutzer einen Staat namens "Texas" erstellen. Wenn diese Anwendung intern verwendet wird, ist es dem Benutzer egal, ob der Staat "texas" oder "Texas" oder "teXas" geschrieben wird

Wichtig ist jedoch, dass das System die Erstellung von "texas" verhindern sollte, wenn "Texas" bereits in der Datenbank vorhanden ist.

Wenn das Modell wie folgt aussieht:

class State(models.Model):
    name = models.CharField(max_length=50, unique=True)

Die Einzigartigkeit würde in Postgres zwischen Groß- und Kleinschreibung unterscheiden, d.h. Postgres würde dem Benutzer erlauben, sowohl "texas" als auch "Texas" zu erstellen, da sie als eindeutig gelten.

Was kann in dieser Situation getan werden, um ein solches Verhalten zu verhindern? Wie kann man vorgehen, wenn man Fall- unauffällig Einzigartigkeit mit Django und Postgres

Im Moment tue ich Folgendes, um die Erstellung von Fällen zu verhindern. unsensiblen Duplikaten zu verhindern.

class CreateStateForm(forms.ModelForm):
    def clean_name(self):
        name = self.cleaned_data['name']
        try:
            State.objects.get(name__iexact=name)
        except ObjectDoesNotExist:
            return name
        raise forms.ValidationError('State already exists.')

    class Meta:
        model = State

Es gibt eine Reihe von Fällen, in denen ich diese Prüfung durchführen muss, und ich bin nicht scharf darauf, überall ähnliche iexact-Prüfungen schreiben zu müssen.

Ich frage mich nur, ob es eine eingebaute oder besseren Weg gibt? Vielleicht würde db_type helfen? Vielleicht gibt es auch eine andere Lösung?

5voto

Michal Čihař Punkte 9417

Neben der bereits erwähnten Option, die Speicherung zu überschreiben, können Sie einfach alle Texte in der Datenbank in Kleinbuchstaben speichern und sie bei der Anzeige groß schreiben.

class State(models.Model):
    name = models.CharField(max_length=50, unique=True)

    def save(self, force_insert=False, force_update=False):
        self.name = self.name.lower()
        super(State, self).save(force_insert, force_update)

3voto

Sie können lookup='iexact' in UniqueValidator auf serializer, wie folgt verwenden:

class StateSerializer(serializers.ModelSerializer): 
    name = serializers.CharField(validators=[
    UniqueValidator(
        queryset=models.State.objects.all(),lookup='iexact'
    )]

django Version: 1.11.6

2voto

Levi Payne Punkte 327

Wenn Sie keine postgres-spezifische Lösung verwenden wollen, können Sie einen eindeutigen Index für das Feld mit upper() um die Eindeutigkeit auf Datenbankebene zu erzwingen, dann erstellen Sie eine benutzerdefinierte Field Mixin, das die Funktionen von get_lookup() um Suchvorgänge, bei denen die Groß-/Kleinschreibung beachtet wird, in ihre Versionen ohne Groß-/Kleinschreibung umzuwandeln. Das Mixin sieht wie folgt aus:

class CaseInsensitiveFieldMixin:
    """
    Field mixin that uses case-insensitive lookup alternatives if they exist.
    """

    LOOKUP_CONVERSIONS = {
        'exact': 'iexact',
        'contains': 'icontains',
        'startswith': 'istartswith',
        'endswith': 'iendswith',
        'regex': 'iregex',
    }

    def get_lookup(self, lookup_name):
        converted = self.LOOKUP_CONVERSIONS.get(lookup_name, lookup_name)
        return super().get_lookup(converted)

Und Sie verwenden es so:

from django.db import models

class CICharField(CaseInsensitiveFieldMixin, models.CharField):
    pass

class CIEmailField(CaseInsensitiveFieldMixin, models.EmailField):
    pass

class TestModel(models.Model):
    name = CICharField(unique=True, max_length=20)
    email = CIEmailField(unique=True)

Sie können mehr über diesen Ansatz lesen aquí .

1voto

Rishabh Manocha Punkte 2877

Sie können dies tun, indem Sie die Speichermethode des Modells überschreiben - siehe die docs . Sie würden im Grunde so etwas tun wie:

class State(models.Model):
    name = models.CharField(max_length=50, unique=True)

    def save(self, force_insert=False, force_update=False):
        if State.objects.get(name__iexact = self.name):
            return
        else:
            super(State, self).save(force_insert, force_update)

Es kann auch sein, dass ich mich irre, aber der kommende SoC-Zweig zur Modellvalidierung wird es uns ermöglichen, dies einfacher zu tun.

0voto

Jorge Punkte 47

Die Lösung von Suhail hat bei mir funktioniert, ohne dass ich citext aktivieren musste, eine ziemlich einfache Lösung, nur eine saubere Funktion und statt capitalize habe ich upper() . Die Lösung von Mayuresh funktioniert auch, allerdings wurde das Feld von CharField à TextField .

class State(models.Model):

    name = models.CharField(max_length=50, unique=True)

    def clean(self):
        self.name = self.name.upper()

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X