Django Many-to-Many relation insertion control

后端 未结 2 679
再見小時候
再見小時候 2021-01-12 15:40

I have the following models:

class Item(models.Model):
    # fields
    # ...

class Collection(models.Model):
    ite         


        
相关标签:
2条回答
  • 2021-01-12 16:24

    I think the best way to approach both of your desired behaviors is not with signals, but rather with an overridden save() and delete() method on the through table which you would define explicitly using the argument through see: https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ManyToManyField.through. and this: https://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methods

    Something like this:

    # -*- coding: utf-8 -*-
    
    from django.db import models
    
    
    class Item(models.Model):
        # fields
        # ...
    
    class Collection(models.Model):
        items = models.ManyToManyField(Item, related_name="collections", through="CollectionItem")
        # other fields
        # ...
    
    class CollectionItem(models.Model):
        collection = models.ForeignKey(Collection)
        item = models.ForeignKey(Item)
    
        def save(self, *args, **kwargs):
            # Only allow this relationship to be created on some_condition
            # Part 1 of your question.
            if some_condition:
                super(CollectionItem, self).save(*args, **kwargs)
    
                # Update some fields on Collection when this
                # relationship is created
                # Part 2 of your question (1/2)
                self.Collection.updateSomeFields()
    
        def delete(self, *args, **kwargs):
            collection = self.collection
            super(CollectionItem, self).delete(*args, **kwargs)
    
            # Update some fields on Collection when this relationship
            # is destroyed.
            # Part 2 of your question (2/2)
            collection.updateSomeFields()
    

    Incidentally, you'll find that adding a relationship will cause a save-signal on this through model.

    And, regarding signals, once you have the through table in place, you'd be able to listen for pre_save and/or post_save signals, but neither of them will allow you to directly veto the creation of the relationship.

    If one or both of your models are supplied by a 3rd party and you really cannot create the through table, then, yes, the signal route may be the only way to go.

    https://docs.djangoproject.com/en/dev/ref/signals/#m2m-changed

    In which case, you could listen for the m2m_changed event and trigger updates to your collection objects (part 2 of your question) and retroactively delete inappropriately created relationships (part 1 of your question). However, as this latter bit is a fugly kludgy, I'd stick with the explicit through table if you can.

    0 讨论(0)
  • 2021-01-12 16:40
    1. The pre_save signal is called before saving an instance. But you are not able to abort the save operation from there. A better solution would be to add a new method to your Collection model, which is responsible for checking if an Item can be added:

      class Collection(models.Model):
          items = models.ManyToManyField(Item, related_name="collections")
      
          ...
      
          def add_item(self, item):
              if check_if_item_can_be_added(item):
                  items.add(item)
                  self.save()
      
      def check_if_item_can_be_added(self, item):
          # do your checks here
      
    2. When adding an instance to a m2m field, the save method does not get called. You are right, the m2m_changed signal is the way to go. You can safely update the collection instance in there.

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