Festlegen des Standardwerts für das Fremdschlüsselattribut


75

Was ist der beste Weg, um einen Standardwert für ein Fremdschlüsselfeld in einem Modell festzulegen? Angenommen, ich habe zwei Modelle Studentund einen ExamSchüler exam_takenals Fremdschlüssel. Wie würde ich idealerweise einen Standardwert dafür festlegen? Hier ist ein Protokoll meiner Bemühungen

class Student(models.Model):
   ....
   .....
   exam_taken = models.ForeignKey("Exam", default=1)

Funktioniert, aber ahnen Sie, es gibt einen besseren Weg.

def get_exam():
    return Exam.objects.get(id=1)

class Student(models.Model):
    ....
    .....
    exam_taken = models.ForeignKey("Exam", default=get_exam)

Dies schlägt jedoch fehl, da bei der Synchronisierung kein Fehler vorliegt.

Jede Hilfe wäre dankbar.



@NitzanTomer Es für ein AdminModelField. Hatte es vorher gesehen.
Primal Pappachan

5
@TomIngram: default=get_exam()ruft get_examsofort und den Wert fest, während default=get_examspeichert die Methode , die später jedes Mal aufgerufen werden , würde das defaultAttribut verwendet wird, um den Wert zu diesem Zeitpunkt zu erhalten. Es wird oft mit Datetime verwendet, das heißt default=datetime.now, nicht default=datetime.now() .
Chris Pratt

@ TomIngram: Ich diskutiere nicht über die Vorzüge eines Ansatzes gegenüber einem anderen. Mein Punkt war nur, dass es gültig ist, und der Autor scheint es so zu wollen.
Chris Pratt

1
@ TomIngram: Der Unterschied besteht darin, dass das Hinzufügen der Klammer nicht mehr "aufrufbar" ist. Es ist ein statischer Wert, nicht anders als nur eine ganze Zahl dort zu setzen.
Chris Pratt

Antworten:


43

In beiden Beispielen codieren Sie die ID der Standardinstanz fest. Wenn das unvermeidlich ist, würde ich einfach eine Konstante setzen.

DEFAULT_EXAM_ID = 1
class Student(models.Model):
    ...
    exam_taken = models.ForeignKey("Exam", default=DEFAULT_EXAM_ID)

Weniger Code und die Benennung der Konstante machen sie besser lesbar.


Ich erhalte folgende Fehlermeldung: TypeError: Additional arguments should be named <dialectname>_<argument>, got 'default' Mit Code: category_id = db.Column(db.Integer, db.ForeignKey("category.id", default=1), primary_key=True) Können Sie bitte helfen?
ChickenFeet

Übrigens gibt es zwei PKs, so dass der Schlüssel immer noch eindeutig ist.
ChickenFeet

Hallo. Gutes Thema! Aber wie kann ich es standardmäßig ohne "ID" codieren? Ich verwende die Rollenmodellklasse und mache "get_or_create default role" als Standardrollenfeld. Ich sollte ID zurückgeben, aber Objekt zurückgeben
parfeniukink

15

Ich würde die Antwort von @ vault oben leicht ändern (dies könnte eine neue Funktion sein). Es ist auf jeden Fall wünschenswert, das Feld mit einem natürlichen Namen zu bezeichnen. Anstatt das zu überschreiben, Managerwürde ich einfach den to_fieldParameter verwenden ForeignKey:

class Country(models.Model):
    sigla   = models.CharField(max_length=5, unique=True)

    def __unicode__(self):
        return u'%s' % self.sigla

class City(models.Model):
    nome   = models.CharField(max_length=64, unique=True)
    nation = models.ForeignKey(Country, to_field='sigla', default='IT')

12

Ich benutze natürliche Schlüssel, um einen natürlicheren Ansatz zu wählen:

<app>/models.py

from django.db import models

class CountryManager(models.Manager):
    """Enable fixtures using self.sigla instead of `id`"""

    def get_by_natural_key(self, sigla):
        return self.get(sigla=sigla)

class Country(models.Model):
    objects = CountryManager()
    sigla   = models.CharField(max_length=5, unique=True)

    def __unicode__(self):
        return u'%s' % self.sigla

class City(models.Model):
    nome   = models.CharField(max_length=64, unique=True)
    nation = models.ForeignKey(Country, default='IT')

2
Ich habe dies in Django 1.6 versucht, aber ich erhalte die Fehlermeldung "Ungültiges Literal für int () mit Basis 10: 'IT'. (Meine Zeichenfolge ist anders, aber Sie haben die Idee.)
Seth

2
Dies funktionierte jedoch gut und scheint eher pythonisch:default=lambda: Country.objects.filter(sigla='IT').first()
Seth

Seltsam, ich erinnere mich, dies mit Django 1.6 getestet zu haben. Googeln Ich fand es ein Tupelproblem, versuche es: def get_by_natural_key(self, sigla): return (self.get(sigla=sigla),)oder auch mit default=('IT',). Ich
Tresor

7

In meinem Fall wollte ich die Standardeinstellung für eine vorhandene Instanz des zugehörigen Modells festlegen . Da es möglich ist, dass die Examwith-ID 1gelöscht wurde, habe ich Folgendes getan:

class Student(models.Model):
    exam_taken = models.ForeignKey("Exam", blank=True)

    def save(self, *args, **kwargs):
        try:
            self.exam_taken
        except:
            self.exam_taken = Exam.objects.first()
        super().save(*args, **kwargs)

Wenn exam_takennicht vorhanden, django.db.models.fields.related_descriptors.RelatedObjectDoesNotExistwird beim Versuch, darauf zuzugreifen, ausgelöst.


4

Sie könnten dieses Muster verwenden:

class Other(models.Model):
    DEFAULT_PK=1
    name=models.CharField(max_length=1024)

class FooModel(models.Model):
    other=models.ForeignKey(Other, default=Other.DEFAULT_PK)

Natürlich müssen Sie sicher sein, dass die Tabelle von eine Zeile enthält Other. Sie sollten eine Datenmigration verwenden, um sicherzustellen, dass sie vorhanden ist.


3

Wie bereits in der Antwort von @ gareth angedeutet , idist es möglicherweise nicht immer die beste Idee, einen Standardwert fest zu codieren :

Wenn der idWert nicht in der Datenbank vorhanden ist, treten Probleme auf. Selbst wenn dieser bestimmte idWert vorhanden ist, kann sich das entsprechende Objekt ändern. In jedem Fall müssten idSie bei Verwendung eines fest codierten Werts auf Datenmigrationen oder die manuelle Bearbeitung vorhandener Datenbankinhalte zurückgreifen.

Um dies zu verhindern, können Sie get_or_create () in Kombination mit einem uniqueanderen Feld als verwenden id.

So würde ich es machen:

from django.db import models

class Exam(models.Model):
    title = models.CharField(max_length=255, unique=True)
    description = models.CharField(max_length=255)

    @classmethod
    def get_default_pk(cls):
        exam, created = cls.objects.get_or_create(
            title='default exam', defaults=dict(description='this is not an exam'))
        return exam.pk


class Student(models.Model):
    exam_taken = models.ForeignKey(to=Exam, on_delete=models.CASCADE,
                                   default=Exam.get_default_pk)

Hier wird ein Exam.titleFeld verwendet, um ein eindeutiges Objekt abzurufen, und ein Exam.descriptionFeld zeigt, wie wir das defaultsArgument (for get_or_create) verwenden können, um das Standardobjekt vollständig anzugeben Exam.

Beachten Sie, dass wir a zurückgeben pk, wie in den Dokumenten vorgeschlagen :

Für Felder wie ForeignKeydieses, die Modellinstanzen zugeordnet sind, sollten Standardwerte der Wert des Felds sein, auf das sie verweisen ( pksofern nicht to_fieldfestgelegt), anstatt Modellinstanzen.

Beachten Sie auch, dass defaultCallables in Model.__init__()( Quelle ) ausgewertet werden . Wenn Ihr Standardwert also von einem anderen Feld desselben Modells oder vom Anforderungskontext oder vom Status des clientseitigen Formulars abhängt, sollten Sie wahrscheinlich woanders suchen .


Es funktioniert nicht, wenn Sie noch keine Migrationen für mich haben. Ich verwende die Feldrolle für Benutzer, wobei ich get_default_user_rolewie in Ihrem Beispiel den Rückruf als Standardparameter verwende. Also , wenn ich keine Migrationen habe ich bekam no such table: users_roleauf python manage.py makemigration. Aber wenn ich role fieldim Benutzermodell kommentiere und run makemigrations- ist das kein Problem. Nach dem, was ich auskommentiere role fieldund run migrationswieder ohne Probleme. Vielleicht kannst du mich damit aufhalten?
parfeniukink

@parfeniukink: Ich würde gerne helfen, aber es ist ein bisschen schwierig zu sagen, was falsch läuft, ohne Ihren tatsächlichen Code zu sehen. Vielleicht könnten Sie eine neue Frage mit einigen Details erstellen?
DJVG

Oh, vielen Dank, aber ich habe diese Aufgabe beendet)))
parfeniukink

0

Ich suche nach der Lösung in Django Admin , dann habe ich Folgendes gefunden:

class YourAdmin(admin.ModelAdmin)

    def get_changeform_initial_data(self, request):
        return {'owner': request.user}

Dadurch kann ich auch den aktuellen Benutzer verwenden.

Siehe Django-Dokumente


0

Der beste Weg, den ich kenne, ist die Verwendung von Lambdas

class TblSearchCase(models.Model):
    weights = models.ForeignKey('TblSearchWeights', models.DO_NOTHING, default=lambda: TblSearchWeights.objects.get(weight_name='value_you_want'))

So können Sie die Standardzeile angeben.

default=lambda: TblSearchWeights.objects.get(weight_name='value_you_want')
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.