tsmith41094
tsmith41094

Reputation: 89

Private messaging system Django

I've been trying to set up a basic private messaging system in Django using the generic CreateView.

I am currently having trouble with the "Receiver"/"To" field in my form. I tried to make it so it was a drop down field with the options being followers of the logged-in user.

Currently, the field is populating with the correct usernames (in this case, "testuser1") but it is throwing an error saying this field needs to be populated with an instance of the User object.

ValueError: Cannot assign "'testuser1'": "Message.reciever" must be a "User" instance.

Is there a way to have the form pass in the object of the username that is selected?

Model:

class Message(models.Model):
    sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name="sender")
    reciever = models.ForeignKey(User, on_delete=models.CASCADE, related_name="reciever")
    subject = models.CharField(max_length=128, default="-")
    content = models.TextField()
    send_date = models.DateTimeField(default=timezone.now)

User Relationships Model:

class UserRelationships(models.Model):
    user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="following")
    following_user_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="followers")

    created = models.DateTimeField(auto_now_add=True)

UPDATED Form:

class MessageCreateForm(forms.ModelForm):

    class Meta:
        model = Message
        fields = ['sender', 'reciever', 'subject', 'content']
        widgets = {'sender': forms.HiddenInput()}

    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user')
        follower_objects = kwargs.pop('follower_objects')
        super(MessageCreateForm, self).__init__(*args, **kwargs)
        self.fields['reciever'] = RecieverModelChoiceField(queryset=User.objects.filter(username__in=follower_objects))

View:

class MessageCreateView(LoginRequiredMixin, CreateView):
    model = Message
    template_name = 'message/compose.html'
    form_class = MessageCreateForm

    def get_initial(self):
        initial = super().get_initial()
        initial['sender'] = self.request.user
        return initial

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        user = self.request.user
        followers = user.followers.values_list('user_id', flat=True)
        follower_objects = []
        kwargs['user'] = self.request.user
        kwargs['follower_objects'] = follower_objects 
        for id in followers:
            follower = User.objects.get(id=id)
            follower_objects.append(follower)
        return kwargs

    def form_valid(self, form):
        form.instance.user = self.request.user
        return super().form_valid(form)

Upvotes: 1

Views: 180

Answers (1)

NKSM
NKSM

Reputation: 5854

You have to use forms.ModelChoiceField instead of forms.ChoiceField

ForeignKey (model) > ModelChoiceField (form) - Default widget: Select

ModelChoiceField has attribute queryset.

You can filter field reciever.queryset directly in MessageCreateForm.__init__ method.

def __init__(self, *args, **kwargs):
    user = kwargs.pop('user')
    super(MessageCreateForm, self).__init__(*args, **kwargs)

    self.fields['reciever'].queryset = user.followers

UPDATE:

You can set a custom ModelChoiceField that will return any label you want (more info).

from django.forms import ModelChoiceField

class RecieverModelChoiceField(ModelChoiceField):
    def label_from_instance(self, obj):
        return obj.username

or

def __init__(self, *args, **kwargs):
    ....
    self.fields['reciever'].label_from_instance = lambda obj: "%s" % obj.username
    

Upvotes: 1

Related Questions