Django-Rest-Framework POST request to ManyToMany Field - django

I have a django model that is a message. It has a name which is a CharField, then also an array of users which is a ManyToManyField.
So This is what my API looks like:
[
{
"id": 13,
"content": "hej",
"date": "2019-07-09",
"sender": {
"id": 1,
"username": "william"
}
},
{
"id": 14,
"content": "hej william",
"date": "2019-07-09",
"sender": {
"id": 3,
"username": "ryan"
}
}
]
What I've tried to send via postman POST:
{
"content": "Hello",
"sender": {"username": "william"},
"date": "2019-09-02"
}
The Error I get:
sqlite3.IntegrityError: NOT NULL constraint failed: chat_message.sender_id
ManyToManyField(Userprofile=User):
class Message(models.Model):
sender = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name="sendermessage")
content = models.CharField(max_length=500)
date = models.DateField(default=date.today)
canview = models.ManyToManyField(UserProfile, blank=True, related_name="messagecanview")
class Meta:
verbose_name_plural = 'Messages'
def __str__(self):
return "{sender}".format(sender=self.sender)

Assuming that you have a MessageSerializer class implemented, you could override its create() method in order to support writable nested representations:
class MessageSerializer(serializers.ModelSerializer):
...
def create(self, validated_data):
sender_data = validated_data.pop('sender')
sender = UserProfile.objects.create(**sender_data)
return Message.objects.create(sender=sender, **validated_data)
You pop the sender data from the dictionary, create a UserProfile instance with the attributes in there and then attach it to your Message creation.
This will resolve your error since now there is a real sender created before the actual message has been saved.

Related

Django Search in many to many field

I am trying to create a search in my Django Rest Framework application I have a table of follower and following via one model called: UserFollowing and the code is:
class UserFollowing(TimeStampedModel):
user_id = models.ForeignKey(
User, related_name="following", on_delete=models.CASCADE, blank=True)
following_user_id = models.ForeignKey(
User, related_name="followers", on_delete=models.CASCADE, blank=True)
#property
def username(self):
profile_instance = Profile.objects.get(user=self.user_id)
username = profile_instance.profile_username
return username
#property
def name(self):
profile_instance = Profile.objects.get(user=self.user_id)
name = profile_instance.name
return name
class Meta:
unique_together = ("user_id", "following_user_id")
ordering = ["-user_id"]
def __str__(self):
return str(self.id)
Here everything works fine but in my following list API, I am appending username and name of the user. When I am getting a list of follower using:
paginator = PageNumberPagination()
query_set = UserFollowing.objects.filter(user_id=request.user.id)
context = paginator.paginate_queryset(query_set, request)
serializer = UserFollowingSerializer(context, many=True)
Now I want to search via the result my serializer.data look like:
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 233,
"username": "Username1",
"user_profile_image": "url",
"name": "CDE",
"is_active": true,
"created_at": "2020-07-25T06:00:31.786154Z",
"updated_at": "2020-07-25T06:00:31.786185Z",
"user_id": 88,
"following_user_id": 53,
"is_following": true
},
{
"id": 49,
"username": "username",
"user_profile_image": "URL",
"name": "ABC",
"is_active": true,
"created_at": "2020-07-15T08:32:35.352484Z",
"updated_at": "2020-07-15T08:32:35.352517Z",
"user_id": 88,
"following_user_id": 144,
"is_following": true
}
]
}
So based on my output how can I search a result where username = What can be the best way to search in this case.

Django: Make a GET Request to a URL that is advanced

So I have Chat Rooms and I have Messages. Then I have two urls: /messages and /rooms. And these display all your rooms and messages. Also a message can be assigned to a room. So in the Room API I have the messages assigned to that room.
Let's say that the room is called 'Room1' and the messages are 'hey', 'yo' and 'wassup'. If I make a request to just /messages I will get all of the messages. Let's say that only two of the messages are assigned to 'Room1' and the other message is assigned to another room not named.
I want a way to make a get request and only get those two messages assigned to 'Room1 with id = 3' (localhost:8000/rooms/3/messages) instead of: (localhost:8000/messages).
This is an example of when I make a get request to /rooms/3/
{
"id": 3,
"name": "Room 1",
"members": [
{
"id": 1,
"username": "william"
},
{
"id": 2,
"username": "eric"
},
{
"id": 3,
"username": "ryan"
}
],
"messages": [
{
"id": 7,
"content": "hej",
"date": "2019-07-08",
"sender": {
"id": 1,
"username": "william"
}
},
{
"id": 8,
"content": "yoyo",
"date": "2019-07-08",
"sender": {
"id": 2,
"username": "eric"
}
},
{
"id": 9,
"content": "tjo bror",
"date": "2019-07-08",
"sender": {
"id": 3,
"username": "ryan"
}
},
{
"id": 10,
"content": "hej jag heter Eric och jag gar pa polhemskolan i lund och jag ar 17 ar gammal",
"date": "2019-07-08",
"sender": {
"id": 2,
"username": "eric"
}
},
{
"id": 11,
"content": "vi vet hahah",
"date": "2019-07-09",
"sender": {
"id": 1,
"username": "william"
}
},
{
"id": 12,
"content": "amen sluta",
"date": "2019-07-09",
"sender": {
"id": 2,
"username": "eric"
}
}
]
}
This is what I want to get in response if I do rooms/3/messages:
"messages": [
{
"id": 7,
"content": "hej",
"date": "2019-07-08",
"sender": {
"id": 1,
"username": "william"
}
},
{
"id": 8,
"content": "yoyo",
"date": "2019-07-08",
"sender": {
"id": 2,
"username": "eric"
}
},
{
"id": 9,
"content": "tjo bror",
"date": "2019-07-08",
"sender": {
"id": 3,
"username": "ryan"
}
},
{
"id": 10,
"content": "hej jag heter Eric och jag gar pa polhemskolan i lund och jag ar 17 ar gammal",
"date": "2019-07-08",
"sender": {
"id": 2,
"username": "eric"
}
},
{
"id": 11,
"content": "vi vet hahah",
"date": "2019-07-09",
"sender": {
"id": 1,
"username": "william"
}
},
{
"id": 12,
"content": "amen sluta",
"date": "2019-07-09",
"sender": {
"id": 2,
"username": "eric"
}
}
]
}
Django Models:
class UserProfile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
verbose_name_plural = 'All Users'
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_data(sender, update_fields, created, instance, **kwargs):
if created:
user = instance
profile = UserProfile.objects.create(user=user)
class Message(models.Model):
sender = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name="sendermessage")
content = models.CharField(max_length=500)
date = models.DateField(default=date.today)
canview = models.ManyToManyField(UserProfile, blank=True, related_name="messagecanview")
class Meta:
verbose_name_plural = 'Messages'
def __str__(self):
return "{sender}".format(sender=self.sender)
class Room(models.Model):
name = models.CharField(max_length=50)
members = models.ManyToManyField(UserProfile, blank=True)
messages = models.ManyToManyField(Message, blank=True)
class Meta:
verbose_name_plural = 'Rooms'
def __str__(self):
return "{name}".format(name=self.name)enter code here
Django Serializers:
class UserProfileSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username')
class Meta:
model = UserProfile
fields = ('id', 'username')
class MessageSerializer(serializers.ModelSerializer):
sender = UserProfileSerializer()
class Meta:
model = Message
fields = ('id', 'content', 'date', 'sender')
class RoomSerializer(serializers.ModelSerializer):
messages = MessageSerializer(many=True)
members = UserProfileSerializer(many=True)
class Meta:
model = Room
fields = ('id', 'name', 'members', 'messages')
Django Views:
class UserProfileView(viewsets.ModelViewSet):
http_method_names = ['get', 'post', 'put', 'delete', 'patch']
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
class MessageView(viewsets.ModelViewSet):
http_method_names = ['get', 'post', 'put', 'delete', 'patch']
queryset = Message.objects.all()
serializer_class = MessageSerializer
class UserMessageView(MessageView):
def get_queryset(self):
return Message.objects.filter(canview__user=self.request.user)
class RoomView(viewsets.ModelViewSet):
http_method_names = ['get', 'post', 'put', 'delete', 'patch']
queryset = Room.objects.all()
serializer_class = RoomSerializer
class UserRoomView(RoomView):
def get_queryset(self):
return Room.objects.filter(members__user=self.request.user)
Django Urls:
router = routers.DefaultRouter()
router.register('users', views.UserProfileView),
router.register('rooms', views.UserRoomView),
router.register('messages', views.UserMessageView),
urlpatterns = [
path('', include(router.urls)),
]
To get all Messages assigned to a room, let's:
Install django-filter:
pip install django-filter
Modify the Room model to specify a related_name:
class Room(models.Model):
name = models.CharField(max_length=50)
members = models.ManyToManyField(UserProfile, blank=True)
messages = models.ManyToManyField(Message, blank=True, related_name='rooms')
# ^^^^^^^^^^^^^^^^^^^^^^
Enable filtering for the rooms related field:
import django_filters
import rest_framework.filters
[...]
class MessageView(viewsets.ModelViewSet):
# vvvvvvvvvvv I don't think this line is needed vvvvvvvvvvvvvv
# http_method_names = ['get', 'post', 'put', 'delete', 'patch']
queryset = Message.objects.all()
serializer_class = MessageSerialize
filter_backends = (
django_filters.rest_framework.DjangoFilterBackend,
rest_framework.filters.OrderingFilter,
)
filter_fields = ['rooms']
Then, you can request all messages for that room with a GET to:
localhost:8000/messages/?rooms=3
Comment question:
You also need to expose the Message object's sender field. Currently it is aliased:
class MessageSerializer(serializers.ModelSerializer):
# vvvv vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
sender_obj = UserProfileSerializer(source='sender', read_only=True)
class Meta:
model = Message
fields = ('id', 'content', 'date', 'sender', 'sender_obj')
# ^^^^^^^^^^^^^^
Then you can POST to /message with the data {"content": "blah", "date": "2019-07-09","sender": 1}

Post Request to Django Rest Api that has a filter. So messages/?room=4

My Backend is built like this. Every 'Room' has 'Messages' And Every Message has a Sender (person who sends it).
I want to make a post request to messages/?room=4. So I want to basically add a message directly to the room with id=4.
//models
class UserProfile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
verbose_name_plural = 'All Users'
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_data(sender, update_fields, created, instance, **kwargs):
if created:
user = instance
profile = UserProfile.objects.create(user=user)
class Message(models.Model):
sender = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name="sendermessage")
content = models.CharField(max_length=500)
date = models.DateField(default=date.today)
canview = models.ManyToManyField(UserProfile, blank=True, related_name="messagecanview")
class Meta:
verbose_name_plural = 'Messages'
def __str__(self):
return "{sender}".format(sender=self.sender)
class Room(models.Model):
name = models.CharField(max_length=50)
members = models.ManyToManyField(UserProfile, blank=True, related_name='room')
messages = models.ManyToManyField(Message, blank=True, related_name='room')
class Meta:
verbose_name_plural = 'Rooms'
def __str__(self):
return "{name}".format(name=self.name)
//serializers, views
class UserProfileSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username')
class Meta:
model = UserProfile
fields = ('id', 'username')
class MessageSerializer(serializers.ModelSerializer):
sender_obj = UserProfileSerializer(source='sender', read_only=True)
class Meta:
model = Message
fields = ('id', 'content', 'date', 'sender', 'sender_obj')
class RoomSerializer(serializers.ModelSerializer):
messages = MessageSerializer(many=True, read_only=True)
members = UserProfileSerializer(many=True, read_only=True)
class Meta:
model = Room
fields = ('id', 'name', 'members', 'messages')
class UserProfileView(viewsets.ModelViewSet):
http_method_names = ['get', 'post', 'put', 'delete', 'patch']
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
filter_backends = (
django_filters.rest_framework.DjangoFilterBackend,
rest_framework.filters.OrderingFilter,
)
filter_fields = ['room']
class MessageView(viewsets.ModelViewSet):
http_method_names = ['get', 'post', 'put', 'delete', 'patch']
queryset = Message.objects.all()
serializer_class = MessageSerializer
filter_backends = (
django_filters.rest_framework.DjangoFilterBackend,
rest_framework.filters.OrderingFilter,
)
filter_fields = ['room']
class UserMessageView(MessageView):
def get_queryset(self):
return Message.objects.filter(canview__user=self.request.user)
class RoomView(viewsets.ModelViewSet):
http_method_names = ['get', 'post', 'put', 'delete', 'patch']
queryset = Room.objects.all()
serializer_class = RoomSerializer
class UserRoomView(RoomView):
def get_queryset(self):
return Room.objects.filter(members__user=self.request.user)
API all messages:
[
{
"id": 7,
"content": "hej",
"date": "2019-07-08",
"sender": 1,
"sender_obj": {
"id": 1,
"username": "william"
}
},
{
"id": 8,
"content": "yoyo",
"date": "2019-07-08",
"sender": 2,
"sender_obj": {
"id": 2,
"username": "eric"
}
},
{
"id": 9,
"content": "tjo bror",
"date": "2019-07-08",
"sender": 3,
"sender_obj": {
"id": 3,
"username": "ryan"
}
},
{
"id": 10,
"content": "hej jag heter Eric och jag gar pa polhemskolan i lund och jag ar 17 ar gammal",
"date": "2019-07-08",
"sender": 2,
"sender_obj": {
"id": 2,
"username": "eric"
}
},
{
"id": 11,
"content": "vi vet hahah",
"date": "2019-07-09",
"sender": 1,
"sender_obj": {
"id": 1,
"username": "william"
}
},
{
"id": 12,
"content": "amen sluta",
"date": "2019-07-09",
"sender": 2,
"sender_obj": {
"id": 2,
"username": "eric"
}
},
{
"id": 13,
"content": "hej",
"date": "2019-07-09",
"sender": 1,
"sender_obj": {
"id": 1,
"username": "william"
}
},
{
"id": 14,
"content": "hej william",
"date": "2019-07-09",
"sender": 3,
"sender_obj": {
"id": 3,
"username": "ryan"
}
}
]
API messages/?room=4
[
{
"id": 7,
"content": "hej",
"date": "2019-07-08",
"sender": 1,
"sender_obj": {
"id": 1,
"username": "william"
}
},
{
"id": 13,
"content": "hej",
"date": "2019-07-09",
"sender": 1,
"sender_obj": {
"id": 1,
"username": "william"
}
},
{
"id": 14,
"content": "hej william",
"date": "2019-07-09",
"sender": 3,
"sender_obj": {
"id": 3,
"username": "ryan"
}
}
]
If someone has a solution it would help loads. I need to get this to work because its a chat application I am developing on flutter- mobile.
Change your MessageSerializer to expose the room related_field ( which is a ManyToManyField and thus each message can be associated to multiple rooms...):
class MessageSerializer(serializers.ModelSerializer):
[...]
fields = ('id', 'content', 'date', 'sender', 'sender_obj', 'room')
# ^^^^^^^^
POST to your /message interface like:
{
"content": "foodefafa",
"sender": 17,
"rooms":[42]
}

How to serialize ForeignKey in Django rest?

I'm using the latest version of django rest framework.
I have this model:
class Subscriptions(models.Model):
subs_list = models.ForeignKey(SubsList, verbose_name='Subscription list', on_delete=models.CASCADE, related_name='subs_list') # идентификатор подписного листа
subscriber = models.ForeignKey(Subscribers, verbose_name='Subscriber', on_delete=models.CASCADE) # идентификатор подписчика
created_date = models.DateTimeField(verbose_name='Created date', auto_now=True) # дата добавления подписчика в подписной лист
deleted = models.NullBooleanField(verbose_name='Deleted') # True-удален из подписного листа, False/null-в подписном листе
How do I serialize it? The main question is how to serialize ForeignKey, that would be associated with the query related data, i.e. NOT:
"id": 29,
"created_date": "2018-03-01T14:28:41.237742Z",
"deleted": false,
"subs_list": 1,
"subscriber": 1
but like this
"id": 29,
"subs_list": {
"id": 1,
"uuid": "d183bab7-af26-48f8-9ef5-ea48e09a95a9",
"name": "TEST",
"description": "TEST",
"created_date": "2018-03-01T13:15:18.808709Z",
"deleted": null,
"user": 6
},
"subscriber": {
"id": 1,
"bot_id": "1",
"name_messenger": "11",
"username": "1",
"first_name": "1",
"last_name": "1",
"created_date": "9999-03-01T16:47:51.440000Z",
"subscribed": true,
"chat_bot": "1",
"phone": "1",
"user": 1
},
"created_date": "2018-03-01T14:28:41.237742Z",
"deleted": false
I have such a serializer:
...
class SubscriptionsSerializer(serializers.ModelSerializer):
subs_list = SubsListSerializer(read_only=True)
subscriber = SubscribersSerializer(read_only=True)
class Meta:
model = Subscriptions
fields = '__all__'
When get requests everything is ok, but how to update and add data is not clear, error:
IntegrityError at /subscriptions/subscriptions/
null value in column "subs_list_id" violates not-null constraint
DETAIL: Failing row contains (41, 2018-03-01 16:10:02.383625+00, f, null, null).
I struggle with this problem for a very long time, read all the related answers, but there is no clarity.
remove read_only=True and change your serializer.py as below
class SubscriptionsSerializer(serializers.ModelSerializer):
subs_list = SubsListSerializer()
subscriber = SubscribersSerializer()
class Meta:
model = Subscriptions
fields = '__all__'
def create(self, validated_data):
sub_lst = SubsList.objects.create(**validated_data['subs_list'])
subscriber = Subscribers.objects.create(**validated_data['subscriber'])
return Subscriptions.objects.create(subs_list=sub_lst, subscriber=subscriber, deleted=validated_data['deleted'])
And your creation payload will be like this,
{
"subs_list": {
"uuid": "d183bab7-af26-48f8-9ef5-ea48e09a95a9",
"name": "TEST",
"description": "TEST",
# etc etc
},
"subscriber": {
"bot_id": "1",
"name_messenger": "11",
"username": "1",
# etc etc
},
"deleted": null
}
I think you have several ForeignKey relationships are there, so you have to map those things carefully in create()
Similar way, you can override update() and which can be used while API updation too.
See this official doc for more info

Inserting foreign key in django rest framework

Here is the code that i am using for serializer
class CitySerializer(serializers.ModelSerializer):
addedBy = serializers.ReadOnlyField(source='addedBy.username')
country = CountrySerializer()
class Meta:
model = City
fields = ('id','name','slug','country','addedBy','createdAt')
class ActivitySerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
tag = ActivityTagSerializer()
location = CitySerializer()
class Meta:
model = Activity
fields = ('id','title','slug','description','location','tag','owner','createdAt')
i am able to insert data if i remove tag and location serializer by adding there pk in form but not with them being serialized. Here is the views that are being used
class ActivityList(generics.ListCreateAPIView):
queryset = Activity.objects.all()
serializer_class = ActivitySerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class ActivityDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Activity.objects.all()
serializer_class = ActivitySerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
The owner field is read-only so thats why had to create a separate insert for the same. Now here are the outputs from the two case , with and without reference to other serializer (tag and location) in ActivitySerializer
{
"id": 3,
"title": "sdfsdf",
"slug": "sdfsdf",
"description": "sdfsdfsdf",
"location": {
"id": 2,
"name": "goa",
"slug": "goa",
"country": {
"id": 1,
"name": "india",
"slug": "india",
"addedBy": "georoot",
"createdAt": "2016-06-17T05:56:30.099121Z"
},
"addedBy": "georoot",
"createdAt": "2016-06-17T06:01:12.771079Z"
},
"tag": {
"id": 3,
"title": "temp",
"slug": "temp",
"addedBy": "georoot",
"createdAt": "2016-06-17T06:03:44.647455Z"
},
"owner": "georoot",
"createdAt": "2016-06-17T06:24:50.006344Z"
}
This is with reference to serializer which is the output view that i want, but insert not working in this case
and this is without reference to the serializer
{
"id": 3,
"title": "sdfsdf",
"slug": "sdfsdf",
"description": "sdfsdfsdf",
"location": 2,
"tag": 3,
"owner": "georoot",
"createdAt": "2016-06-17T06:24:50.006344Z"
}
Here i can change the values of tag and location by passing in the pk.
How can i do the same while referencing to other serializer as in the previous output
EDIT
structure for models
class City(models.Model):
name = models.CharField(max_length=200)
slug = AutoSlugField(populate_from='name')
country = models.ForeignKey(Country)
addedBy = models.ForeignKey('auth.user')
createdAt = models.DateTimeField(auto_now_add=True, blank=True)
class Activity(models.Model):
owner = models.ForeignKey('auth.user')
title = models.CharField(max_length=200)
slug = AutoSlugField(populate_from='title')
description = models.TextField()
location = models.ForeignKey(City)
tag = models.ForeignKey(ActivityTag)
createdAt = models.DateTimeField(auto_now_add=True, blank=True)