0

I have a serializer, I'm trying to add an additional field, from a different model. The goal is to add the username from the user who requested the data.

Here is my serializer, I try to accomplish my goal using the username variable and adding it to fields.

class BucketListSerializer(serializers.ModelSerializer, EagerLoadingMixin):

    stock_count = serializers.SerializerMethodField()
   
    username = serializers.CharField(source='User.username', read_only=True)

        model = Bucket
        fields = ('id','username','category','name','stock_count',
                'stock_list','owner','admin_user',
                'guest_user','about','created','slug',
                'bucket_return', 'bucket_sectors','bucket_pos_neg')

    def get_stock_count(self, obj):
        if obj.stock_count:
            return obj.stock_count
        return 0

There aren't any syntax errors with this serializer, however the username field is not working. There is no data in the dictionary returned with the key username

User model:

class CustomAccountManager(BaseUserManager):

    def create_superuser(self, email, username, first_name, last_name, password, **other_fields):

        other_fields.setdefault('is_staff', True)
        other_fields.setdefault('is_superuser', True)
        other_fields.setdefault('is_active', True)

        if other_fields.get('is_staff') is not True:
            raise ValueError(
                'Superuser must be assigned to is_staff=True.')
        if other_fields.get('is_superuser') is not True:
            raise ValueError(
                'Superuser must be assigned to is_superuser=True.')

        return self.create_user(email, username, first_name, last_name, password, **other_fields)

    def create_user(self, email, username, first_name, last_name, password, **other_fields):

        if not email:
            raise ValueError(_('You must provide an email address'))

        if not username:
            raise ValueError(_('You must provide a username'))

        if not first_name:
            raise ValueError(_('You must provide your first name'))

        if not last_name:
            raise ValueError(_('You must provide your last name'))
        
        if not password:
            raise ValueError(_('You must provide a valid password'))

        email = self.normalize_email(email)
        user = self.model(email=email, username=username,
                          first_name=first_name, last_name=last_name, password=password, **other_fields)
        user.set_password(password)
        user.save()
        return user


class User(AbstractBaseUser, PermissionsMixin):

    email = CIEmailField(_('email address'), unique=True)
    username = CICharField(max_length=150, unique=True)
    first_name = models.CharField(max_length=150, blank=True)
    last_name = models.CharField(max_length=150, blank=True)
    start_date = models.DateTimeField(default=timezone.now)
    subscribed = models.BooleanField(default=False)
    is_active = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)

    objects = CustomAccountManager()


    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username', 'first_name', 'last_name','password']

    def __str__(self):
        return self.username

Here is the view used:

class BucketList(generics.ListAPIView):
    permission_classes = [IsAuthenticated]
    serializer_class = BucketListSerializer
    filter_backends = [OwnerOrUserFilterBackend]
    queryset = Bucket.objects.all()

how can I simply add the username of a user to my serializer class above?


EDIT:

Here is my bucket model:


class Bucket(models.Model):

    category_options = (
        ('personal', 'Personal'),
        ('social', 'Social'),
    )


    class BucketObjects(models.Manager):
        def get_queryset(self):
            return super().get_queryset()

    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='buckets')
    admin_user = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='admin_user')
    guest_user = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='guest_user', blank=True)
    category = models.CharField(max_length=30, choices=category_options)
    name = models.CharField(max_length=35)
    created = models.DateTimeField(default=timezone.now, blank=True)
    slug = AutoSlugField(populate_from = "random_string", blank=True) 
    stock_count = models.IntegerField(blank=True, null=True)
    stock_list = ArrayField(models.CharField(max_length=6,null=True),size=30,null=True, blank=True)
    about = models.CharField(max_length=200)

    objects = models.Manager()
    bucketobjects = BucketObjects()

EDIT #2

class BucketListSerializer(serializers.ModelSerializer):
   
    user = serializers.SerializerMethodField('get_user')

    class Meta:
        model = Bucket
        fields = (...'user')


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

andres
  • 1,558
  • 7
  • 24
  • 62

3 Answers3

1
username = serializers.SerializerMethodField('_user')

def _user(self, obj):
    request = self.context.get('request', None)
    if request and request.user:
        return request.user.username
    return ''

This will work for you.

paras chauhan
  • 777
  • 4
  • 13
0

The correct traverse attribute should be

class BucketListSerializer(serializers.ModelSerializer, EagerLoadingMixin):
    ...
    username = serializers.CharField(source='owner.username', read_only=True)
    ...
panchicore
  • 11,451
  • 12
  • 74
  • 100
  • Hi panchicore, thank you for the help. This works, however case when the `owner` is NOT the requested user, it doesnt. I'm trying to get the username of the request provided by the user. – andres Mar 31 '21 at 21:42
  • interesting use case, especially because you have to set the username somewhere since this is not stored on the model, I would do an experiment, create an attribute to the Bucket model, so you can have a serializer field with a source mapped to it so you can set the request.user.username value, then override the ListAPIView (I think is the get_object method https://www.django-rest-framework.org/api-guide/generic-views/#get_objectself) and set the requested user username there. – panchicore Mar 31 '21 at 22:09
0

I think you can use context in your serializer:

user = self.context['request'].user

So if you want to display the username you can create username as SerailizerMethodField and return the username from the user object which you get from request context.

class BucketListSerializer(serializers.ModelSerializer):

user = serializers.SerializerMethodField(read_only=True)

class Meta:
    model = Bucket
    fields = (...'user')


def get_user(self, instance):
    user = self.context['request'].user
    return user
Neeraj
  • 975
  • 4
  • 10
  • Please see my new update. I've tried implementing the way you recommend but Im doing it wrong? – andres Apr 01 '21 at 04:46
  • "user" is your object, use debugger and check if its returning anything. If im not mistaken user should be an object and you must return user.username. Run your code in debugger mode and add breakpoint on return – Neeraj Apr 01 '21 at 05:02
  • not quite sure I understand you, could you show me instead perhaps? – andres Apr 01 '21 at 06:55
  • You can refer this: https://stackoverflow.com/questions/11254491/how-do-i-debug-breakpoint-my-django-app-using-pycharm Use debugger in pycharm (not sure but it probably can be done in other editors too), its really helpful to know what your code is doing. I've updated my response you can try that. – Neeraj Apr 01 '21 at 07:08