2017-02-13 6 views
0

Ich arbeite an einem Projekt mit einem Channel/Post-Mechanismus. Ich implementiere derzeit Funktionen zum "Löschen" von Beiträgen, indem ich ein Feld mit dem Namen "gelöscht" in einen Zeitstempel des Zeitpunkts des Löschens einstelle. Es muss für die historische Bedeutung (letzte Aktivität) aufbewahrt werden und Personen, die inflammatorisch sind, verfolgen (der Dienst ist ein privilegierter Teil des gesamten Projekts).Django ORM Filtern von Objekten nach Feldwerten?

models.py

class Post(models.Model): 
    deleted = models.DateTimeField(null=True, blank=True) 
    created_date = models.DateTimeField(auto_now_add=True) 
    updated_date = models.DateTimeField(auto_now_add=True) 
    post_text = models.CharField(max_length=1000) 
    post_to = models.ForeignKey('Channel', related_name='post_to_channel') 
    post_from = models.ForeignKey(User, related_name='user_from_post') 

class Channel(models.Model): 
    created_date = models.DateTimeField(auto_now_add=True) 
    channel_title = models.CharField(max_length=50, unique=True) 
    channel_description = models.CharField(max_length=100) 
    channel_creator = models.ForeignKey(User) 
    channel_private = models.BooleanField(default=False) 
    channel_users = models.ManyToManyField(User, through='Membership', related_name='channels', blank=True) 
    channel_posts = models.ManyToManyField(Post, related_name='channel_post', blank=True) 
    initial_invites = models.ForeignKey(User, null=True, blank=True, related_name='initial_invites') 
    objects = models.Manager() 

class Membership(models.Model): 
    channel = models.ForeignKey(Channel) 
    channel_user = models.ForeignKey(User, related_name='channel_membership') 
    join_date = models.DateTimeField(auto_now_add=True) 

ich einen Endpunkt haben, die aktualisiert das „gelöschte“ -Feld der „Post“ Objekt ein Zeitstempel von null zu sein. Wie vermeide ich es dann, alle Posts zurückzugeben, die einen Timestamp haben, wenn sie auf der "Channel" -Ebene abgefragt werden, die Posts durch ihre Beziehung geladen werden und einen separaten Serializer? Grundsätzlich muss ich in der Lage sein, "Channel" abzufragen und eine Liste der Kanäle zu erhalten, in denen ich ein Teil bin, und dann durch ihre Beziehung von ManyToMany mit "Post" alle zugehörigen Posts mit Ausnahme derjenigen, die einen Wert im Feld "deleted" haben . Ist dies möglich, ohne das ORM für jeden Kanal aufzurufen und anschließend die gelöschten Beiträge herauszufiltern? Um klar zu sein, der "Channel" sollte immer noch durchkommen, wenn es einen gelöschten Post gibt, nur der "Post" selbst sollte ausgeblendet werden.

Wenn ich nicht genug Informationen zur Verfügung gestellt habe, lassen Sie mich wissen, was Sie sehen müssen. Wenn die Antwort lautet, dass dies ohne wiederholte ORM-Aufrufe nicht möglich ist, würden andere Optionen empfohlen, einschließlich der Möglichkeit, die Posts auf ein Duplikatmodell zu "archivieren", ohne dass in diesen Situationen Hooks aufgerufen werden müssen und Löschen des Originals.

Edit 1: eine Eigenschaft hinzufügen, wie in der gewählten Antwort erwähnt, aber es kann nicht self.channel_posts sehen. models.py

class Channel(models.Model): 
    created_date = models.DateTimeField(auto_now_add=True) 
    channel_title = models.CharField(max_length=50, unique=True) 
    channel_description = models.CharField(max_length=100) 
    channel_creator = models.ForeignKey(User) 
    channel_private = models.BooleanField(default=False) 
    channel_users = models.ManyToManyField(User, through='Membership', related_name='channels', blank=True) 
    channel_posts = models.ManyToManyField(Post, related_name='channel_post', blank=True) 
    initial_invites = models.ForeignKey(User, null=True, blank=True, related_name='initial_invites') 
    objects = models.Manager() 
    @property 
    def get_viewable_posts(self): 
     print (self.channel_users) # prints [] 
     print (self.channel_posts) # prints [] 
     return CourtyardPost.objects.filter(deleted__isnull=True, courtyard_post_to=self.pk) #works, but adds IO strain 

Derzeit wie oben gezeigt, es ist ein ORM Anruf pro Kanal, aber das Drucken self.channel_posts oder self.channel_users gibt eine leere Liste, in allen Szenarien.

class CourtyardChannelSerializer(serializers.ModelSerializer): 
    current_user = serializers.SerializerMethodField('_user') 
    post_to_channel = CourtyardPostSerializer(many=True, source='get_viewable_posts', read_only=True) 
    most_recent = CourtyardPostSerializer(many=True, source='latest_post', read_only=True) 
    channel_users = CourtyardUserSerializer(many=True, read_only=True) 
) 
invite_list = serializers.ReadOnlyField(source='get_invitation_set', required=False) 

    def _user(self, obj): 
     user = self.context['request'].user.username 
     return user 

Antwort

1
class Channel(models.Model): 
    created_date = models.DateTimeField(auto_now_add=True) 
    channel_title = models.CharField(max_length=50, unique=True) 
    channel_description = models.CharField(max_length=100) 
    channel_creator = models.ForeignKey(User) 
    channel_private = models.BooleanField(default=False) 
    channel_users = models.ManyToManyField(User, through='Membership', related_name='channels', blank=True) 
    channel_posts = models.ManyToManyField(Post, related_name='channel_post', blank=True) 
    initial_invites = models.ForeignKey(User, null=True, blank=True, related_name='initial_invites') 
    objects = models.Manager() 

    @property 
    def active_posts(self): 
     return self.channel_posts.filter(deleted=None) 

sehr einfach, nur eine zusätzliche Eigenschaft hinzugefügt und jetzt können Sie es wie folgt ich eigentlich

channel = Channel.objects.first() 
print(channel.active_posts.count()) 
+0

verwenden lief in ein Problem versucht, diese zu verwenden, wie sie ist, aus irgendeinem Grund 'Selbst .channel_posts' ist ein leeres Array, das am Punkt der Eigenschaft (über einen Druck) gefiltert wird. Am Ende musste ich es in ein gefiltertes Abfrageset aller Posts überarbeiten, aber das wird aufgrund von so vielen DB-Aufrufen nicht gut im Maßstab funktionieren, irgendwelche Ideen, warum es leer angezeigt würde? Deine Antwort ist immer noch die sauberste, ich hoffe nur, dass es funktionieren kann, wie du es geschrieben hast, anstatt meiner improvisierten Version. So oder so, danke für Ihre Hilfe und Zeit! – AntonMiles

+0

Wenn Sie den Code genau so ausführen, wie er ist, würden Sie natürlich active_posts für den "ersten" Kanal in Ihrer Datenbank bekommen. seit Channel.objects.first() gibt ersten Kanal in DB zurück. Könnte es sein, dass der erste Kanal keine Beiträge hat? – Ramast

+0

Ich habe es so angepasst, dass es 'active_posts' als Quelle für die Repräsentation der ManyToMany-Beziehung des Serialisierers verwenden kann. Ich werde die Frage bearbeiten, um dies zu verdeutlichen, aber es sieht in keinem der Kanäle etwas für meinen Standardbenutzer. Wer hatte Beiträge verfügbar, bevor er die Löschfunktion startete, und wer hat seitdem gepostet (was funktioniert und über einen anderen Endpunkt sichtbar ist). Es ist immer noch die beste Antwort, und es für ein Queryset zu tauschen funktioniert, nur in der Hoffnung, den DB IO-Belastung zu reduzieren. * zur besseren Übersicht bearbeitet. – AntonMiles

0

Ich vermute, dass in dem Moment, Sie sind so etwas wie tun: Das Grundstück ist als Teil Django Ruhe Framework Serializer unten als implementiert genannt

def channels_and_posts_for_user(user): 
    for channel in user.channel_membership: 
     posts = channel.channel_posts.all().filter(deleted__isnull=True) 
     channels_and_posts.append(channel, posts) 
    return channels_and_posts 

und Sie würden Möchten Sie diesen Filter bei jedem einzelnen Anruf loswerden?

Es ist ein Schmerz, ich stimme zu, ich habe versucht, etwas Ähnliches mit einer "archivierten" Variable in einigen Modellen für meine Webapp zu tun.

Ich glaube nicht, dass es einen Weg gibt. Sie können einen benutzerdefinierten Manager erstellen wie folgt:

class ChannelManagerWithUndeleted(models.Manager): 
    def get_queryset(self): 
     return super(ChannelManagerWithUndeleted, self).get_queryset().filter(deleted__isnull=True) 

class Channel(models.Model): 
    #... 
    objects = models.Manager() # Default Manager 
    undeleted = EntryManager() # Custom Manager 

und dann die Objekte über Channel.undeleted.all() statt Channel.objects.all direkt zugreifen, aber Sie müssen noch auf related calls anyway diesen neuen Manager spezifizieren, und es endet fast so ausführlich (wenn ein wenig mehr DRY):

channels_and_posts = [] 
for channel in user.channel_membership: 
    posts = channel.channel_posts.all(manager='undeleted').all() 
    channels_and_posts.append(channel, posts) 

Dies ist auch ein relevanter Beitrag: How to use custom manager with related objects?.


Ich denke, es ist kompliziert, weil jeder etwas anderes Verhalten in den verschiedenen Situationen will. z.B. Ich möchte, dass meine archivierten "Ereignisse" weiterhin Berichte ausführen können, aber nicht für den Endbenutzer zur Auswahl angezeigt werden. Daher verwende ich einen benutzerdefinierten Manager, der sich an alle Verbraucher wendet.

Wenn alles, was Sie tun wollen, dass sie für die Berichterstattung später zählen, vielleicht eine Option ist ein channel_deleted_posts ManyToManyField hinzuzufügen und den Beitrag verschieben (ohne ihn zu löschen) channel_posts-channel_deleted_posts. Haken notwendig, fürchte ich.

Ich hoffe wirklich, es gibt eine bessere Antwort und das, was Sie wollen, ist trivial. Ich würde gerne falsch bewiesen werden! :)

Verwandte Themen