Django Model Field Default Based Off Another Field in Same Model

前端 未结 4 1687
挽巷
挽巷 2020-11-27 13:48

I have a model that I would like to contain a subjects name and their initials (he data is somewhat anonymized and tracked by initials).

Right now, I wrote

<         


        
相关标签:
4条回答
  • 2020-11-27 14:14

    Models certainly do have a "self"! It's just that you're trying to define an attribute of a model class as being dependent upon a model instance; that's not possible, as the instance does not (and cannot) exist before your define the class and its attributes.

    To get the effect you want, override the save() method of the model class. Make any changes you want to the instance necessary, then call the superclass's method to do the actual saving. Here's a quick example.

    def save(self, *args, **kwargs):
        if not self.subject_init:
            self.subject_init = self.subject_initials()
        super(Subject, self).save(*args, **kwargs)
    

    This is covered in Overriding Model Methods in the documentation.

    0 讨论(0)
  • 2020-11-27 14:21

    As an alternative implementation of Gabi Purcaru's answer, you can also connect to the pre_save signal using the receiver decorator:

    from django.db.models.signals import pre_save
    from django.dispatch import receiver
    
    
    @receiver(pre_save, sender=Subject)
    def default_subject(sender, instance, **kwargs):
        if not instance.subject_init:
            instance.subject_init = instance.subject_initials()
    

    This receiver function also takes the **kwargs wildcard keyword arguments which all signal handlers must take according to https://docs.djangoproject.com/en/2.0/topics/signals/#receiver-functions.

    0 讨论(0)
  • 2020-11-27 14:24

    I don't know if there is a better way of doing this, but you can use a signal handler for the pre_save signal:

    from django.db.models.signals import pre_save
    
    def default_subject(sender, instance, using):
        if not instance.subject_init:
            instance.subject_init = instance.subject_initials()
    
    pre_save.connect(default_subject, sender=Subject)
    
    0 讨论(0)
  • 2020-11-27 14:30

    Using Django signals, this can be done quite early, by receiving the post_init signal from the model.

    from django.db import models
    import django.dispatch
    
    class LoremIpsum(models.Model):
        name = models.CharField(
            "Name",
            max_length=30,
        )
        subject_initials = models.CharField(
            "Subject Initials",
            max_length=5,
        )
    
    @django.dispatch.receiver(models.signals.post_init, sender=LoremIpsum)
    def set_default_loremipsum_initials(sender, instance, *args, **kwargs):
        """
        Set the default value for `subject_initials` on the `instance`.
    
        :param sender: The `LoremIpsum` class that sent the signal.
        :param instance: The `LoremIpsum` instance that is being
            initialised.
        :return: None.
        """
        if not instance.subject_initials:
            instance.subject_initials = "".join(map(
                    (lambda x: x[0] if x else ""),
                    instance.name.split(" ")))
    

    The post_init signal is sent by the class once it has done initialisation on the instance. This way, the instance gets a value for name before testing whether its non-nullable fields are set.

    0 讨论(0)
提交回复
热议问题