Django: CharField mit fester Länge, wie?

75

Ich hätte gerne ein CharField mit fester Länge in meinem Modell. Mit anderen Worten, ich möchte, dass nur eine bestimmte Länge gültig ist.

Ich habe versucht so etwas zu tun

volumenumber = models.CharField('Volume Number', max_length=4, min_length=4)

aber es gibt mir einen Fehler (es scheint, dass ich sowohl max_length als auch min_length gleichzeitig verwenden kann).

Gibt es einen anderen schnellen Weg?

Vielen Dank

BEARBEITEN:

Nach den Vorschlägen einiger Leute werde ich etwas genauer sein:

Mein Modell ist das:

class Volume(models.Model):
    vid = models.AutoField(primary_key=True)
    jid = models.ForeignKey(Journals, db_column='jid', null=True, verbose_name = "Journal")
    volumenumber = models.CharField('Volume Number')
    date_publication = models.CharField('Date of Publication', max_length=6, blank=True)
    class Meta:
        db_table = u'volume'
        verbose_name = "Volume"
        ordering = ['jid', 'volumenumber']
        unique_together = ('jid', 'volumenumber')
    def __unicode__(self):
        return (str(self.jid) + ' - ' + str(self.volumenumber))

Was ich will ist, dass das volumenumbergenau 4 Zeichen sein muss.

IE, wenn jemand '4b' Django einfügt, gibt einen Fehler aus, weil er eine Zeichenfolge von 4 Zeichen erwartet.

Also habe ich es mit versucht

volumenumber = models.CharField('Volume Number', max_length=4, min_length=4)

aber es gibt mir diesen Fehler:

Validating models...
Unhandled exception in thread started by <function inner_run at 0x70feb0>
Traceback (most recent call last):
  File "/Library/Python/2.5/site-packages/django/core/management/commands/runserver.py", line 48, in inner_run
    self.validate(display_num_errors=True)
  File "/Library/Python/2.5/site-packages/django/core/management/base.py", line 249, in validate
    num_errors = get_validation_errors(s, app)
  File "/Library/Python/2.5/site-packages/django/core/management/validation.py", line 28, in get_validation_errors
    for (app_name, error) in get_app_errors().items():
  File "/Library/Python/2.5/site-packages/django/db/models/loading.py", line 131, in get_app_errors
    self._populate()
  File "/Library/Python/2.5/site-packages/django/db/models/loading.py", line 58, in _populate
    self.load_app(app_name, True)
  File "/Library/Python/2.5/site-packages/django/db/models/loading.py", line 74, in load_app
    models = import_module('.models', app_name)
  File "/Library/Python/2.5/site-packages/django/utils/importlib.py", line 35, in import_module
    __import__(name)
  File "/Users/Giovanni/src/djangoTestSite/../djangoTestSite/journaldb/models.py", line 120, in <module>
    class Volume(models.Model):
  File "/Users/Giovanni/src/djangoTestSite/../djangoTestSite/journaldb/models.py", line 123, in Volume
    volumenumber = models.CharField('Volume Number', max_length=4, min_length=4)
TypeError: __init__() got an unexpected keyword argument 'min_length'

Das erscheint natürlich nicht, wenn ich nur "max_length" ODER "min_length" verwende.

Ich habe die Dokumentation auf der Django-Website gelesen und es scheint, dass ich Recht habe (ich kann nicht beide zusammen verwenden), also frage ich, ob es einen anderen Weg gibt, das Problem zu lösen.

Danke noch einmal

Giovanni Di Milia
quelle

Antworten:

45

Feldinstanzen des CharField-Datenbankmodells haben nur einen max_lengthParameter, wie in den Dokumenten angegeben . Dies liegt wahrscheinlich daran, dass es in SQL nur ein maximales Kontraint-Äquivalent für die Zeichenlänge gibt.

Formularfeld CharField- Objekte haben dagegen einen min_lengthParameter. Sie müssten also eine benutzerdefinierte ModelForm für dieses bestimmte Modell schreiben und das Standardformular für das Administratormodell mit dem benutzerdefinierten überschreiben.

So ähnlich:

# admin.py

from django import forms

...

class VolumeForm(forms.ModelForm):
    volumenumber = forms.CharField(max_length=4, min_length=4)

    class Meta:
        model = Volume


class VolumeAdmin(admin.ModelAdmin):
    form = VolumeForm

...

admin.site.register(Volume, VolumeAdmin)
Haes
quelle
8
Alternativ können Sie einen benutzerdefinierten Validator schreiben, der einen ValidationError auslöst, wenn die Länge nicht 4 ist. Auf diese Weise enthält die Datenbank niemals eine falsche Länge, selbst wenn Ihre VolumeForm nicht verwendet wird: docs.djangoproject.com/en/dev / ref / models / instance /…
Ben
es wird varchar erzeugen, nicht char (im MySQL-Geschmack). Festes Zeichen ist schneller als Varchar.
Sławomir Lenart
89

Sie müssen nicht einmal eine benutzerdefinierte schreiben. Verwenden Sie einfach das, RegexValidatorwas Django liefert.

from django.core.validators import RegexValidator

class MyModel(models.Model):
    myfield = models.CharField(validators=[RegexValidator(regex='^.{4}$', message='Length has to be 4', code='nomatch')])

Aus den Django Docs: class RegexValidator(\[regex=None, message=None, code=None\])

regex: Ein gültiger regulärer Ausdruck, der übereinstimmt. Weitere Informationen zu Regex in Python finden Sie in diesem hervorragenden HowTo: http://docs.python.org/howto/regex.html

message: Die Nachricht wird im Fehlerfall an den Benutzer zurückgegeben.

code: Von ValidationError zurückgegebener Fehlercode. Für Ihren Anwendungsfall nicht wichtig, können Sie ihn weglassen.

Achtung, der von mir vorgeschlagene reguläre Ausdruck erlaubt alle Zeichen, einschließlich Leerzeichen. Um nur alphanumerische Zeichen zuzulassen, ersetzen Sie das '.' mit '\ w' im Regex-Argument. Für andere Anforderungen ReadTheDocs;).

tBuLi
quelle
7
Ziehen Sie dies der akzeptierten Antwort vor, da dadurch die korrekte Länge sichergestellt wird, auch wenn der Benutzer VolumeForm nicht verwendet
@tBuLi Ich habe versucht, nur Ziffern zuzulassen und den regulären Ausdruck in ^ \ d {5} $ geändert, aber es funktioniert nicht. Ich kann das Modell mit beliebigen Zeichen speichern. MyModel (myfield = 'idsj is'). Save ()
Daniil Mashkin
1
für nur alphanumerische Verwendung zulassen: regex = '^ \ w + $'
M Haziq
74

Ähnlich wie oben, aber für das, was es wert ist, können Sie auch mit MinLengthValidator fortfahren, den Django liefert. Hat für mich gearbeitet. Der Code würde ungefähr so ​​aussehen:

from django.core.validators import MinLengthValidator
...
class Volume(models.Model):
volumenumber = models.CharField('Volume Number', max_length=4, validators=[MinLengthValidator(4)])
...
Chetan
quelle
16

Sie können einen benutzerdefinierten Validator schreiben, wie von @Ben vorgeschlagen. Zum Datum dieser Antwort finden Sie die entsprechenden Anweisungen unter https://docs.djangoproject.com/de/dev/ref/validators/.

Der Code wäre ungefähr so ​​(Kopieren vom Link):

from django.core.exceptions import ValidationError

def validate_length(value,length=6):
    if len(str(value))!=length:
        raise ValidationError(u'%s is not the correct length' % value)

from django.db import models

class MyModel(models.Model):
    constraint_length_charField = models.CharField(validators=[validate_length])
tjb
quelle
1
Sie müssen Ihren benutzerdefinierten Validator für die from django.core.validators import MinLengthValidator
Mindestlänge nicht
0

Noch eine Implementierung unter Verwendung eines benutzerdefinierten Modellfelds :

from django.core.validators import BaseValidator
from django.db import models
from django.utils.deconstruct import deconstructible


@deconstructible
class FixedLengthValidator(BaseValidator):
    message = 'Ensure this value has %(limit_value)d character (it has %(show_value)d).'
    code = 'length'

    def compare(self, a, b):
        return a != b

    def clean(self, x):
        return len(x)


class FixedLengthCharField(models.CharField):
    def __init__(self, *args, length, **kwargs):
        self.length = length
        kwargs['max_length'] = length
        super().__init__(*args, **kwargs)
        self.validators.insert(0, FixedLengthValidator(length))

    def deconstruct(self):
        name, path, args, kwargs = super().deconstruct()
        del kwargs['max_length']
        kwargs['length'] = self.length
        return name, path, args, kwargs
dtatarkin
quelle