0

I'm struggling to create users by sending data from Vue.js to Django REST Framework.

First of all, I made my own custom user model with AbstractBaseUser. and migration where successful, It is able to communicate with mysql, vue.js too. the python manage.py createsuperuser works well too.

So the password field created as hashed string automatically whenever I try to use createsupersuser and at the ~/admin domain too.

However when I'm trying to send POST method in Vue.js to user model, It's just putted raw(non hashed) password in it, and not follow my Django default model set up like is_admin = False, is_active = True. These boolean fields would be set always 'False' by Vue axios method

Here is my user model. ./Users/models.py

# Abstracted User fields with options
class User(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(
        max_length=20,
        null=False,
        blank=False,
        unique=True,
        error_messages={'unique': _('이미 존재하는 아이디 입니다. (The account is already existed.)')},
    )
    full_name = models.CharField(
        _('first_name' + 'last_name'),
        max_length=30,
        default='',
    )
    email = models.EmailField(
        _('email address'),
    )
    organization = models.CharField(
        max_length=50,
        null=True,
        blank=True,
    )
    phone = models.CharField(
        max_length=16,
        null=True,
        blank=True,
    )
    is_staff = models.BooleanField(
        _('is_staff'),
        default=False,
    )
    is_active = models.BooleanField(
        _('is_active'),
        default=True,
    )
    created_date = models.DateTimeField(
        _('date joined'),
        auto_now_add=True,
    )
    updated_date = models.DateTimeField(
        auto_now=True,
    )

    # User Management object
    objects = UserManager()

    # This field will be the 'username'
    USERNAME_FIELD = 'username'
    # Required for create user (without username, password)
    REQUIRED_FIELDS = ['full_name', 'email', 'organization', 'phone']

    class Meta:
        db_table = 'Users'
        verbose_name = _('user')
        verbose_name_plural = _('users')

    def __str__(self):
        return self.username

    def get_full_name(self):
        """
        This method is required by Django for things like handling emails.
        Typically this would be the user's first and last name. Since we do
        not store the user's real name, we return their username instead.
        """
        if self.full_name:
            return self.full_name
        return self.email

    def get_short_name(self):
        """
        This method is required by Django for things like handling emails.
        Typically, this would be the user's first name. Since we do not store
        the user's real name, we return their username instead.
        """
        if self.full_name:
            return self.full_name
        return self.email

    def has_perm(self, perm, obj=None):
        return True

    def has_module_perms(self, app_label):
        return True

    @property
    def token(self):
        return self._generate_jwt_token()

    def _generate_jwt_token(self):
        dt = datetime.now() + timedelta(days=1)

        token = jwt.encode({
            'id': self.pk,
            'exp': int(dt.strftime('%s'))
        }, settings.SECRET_KEY, algorithm='HS256')

        return token.decode('utf-8')

And it is my user manager. in same repository.

# Abstracted User manager.
class UserManager(BaseUserManager):
    def create_user(self, username, password=None, full_name=None, organization=None,
                    email=None, phone=None, admin=False, staff=False, active=True):
        if not username:
            raise ValueError('아이디는 필수 항목입니다. (Account is required field.)')
        if not password:
            raise ValueError('비밀번호는 필수 항목입니다. (Password is required field.)')
        if not full_name:
            raise ValueError('사용자 이름은 필수 항목입니다. (Name is required field.)')
        if not email:
            raise ValueError('이메일은 필수 항목입니다. (E-mail is required field.)')
        user_obj = self.model(
            username=username,
            full_name=full_name,
            organization=organization,
            phone=phone,
        )

        # Filled area from user
        # user_obj.full_name = full_name
        # user_obj.organization = organization
        # user_obj.phone = phone
        user_obj.username = username
        user_obj.email = self.normalize_email(email)
        user_obj.set_password(password)

        # Invisible fields
        user_obj.is_superuser = admin
        user_obj.is_staff = staff
        user_obj.is_active = active

        user_obj.save(using=self._db)
        return user_obj

    def create_staffuser(self, username, password=None, full_name=None, organization=None, email=None, phone=None):
        user = self.create_user(
            username=username,
            password=password,
            full_name=full_name,
            email=email,
            organization=organization,
            phone=phone,
            staff=True,
        )
        user.save(using=self._db)
        return user

    def create_superuser(self, username, password=None, full_name=None, organization=None, email=None, phone=None):
        user = self.create_user(
            username=username,
            password=password,
            full_name=full_name,
            email=email,
            organization=organization,
            phone=phone,
            staff=True,
            admin=True,
        )
        user.save(using=self._db)
        return user

And my form python file. ./Users/form.py

class RegisterForm(forms.ModelForm):
    password = forms.CharField(widget=forms.PasswordInput)
    password2 = forms.CharField(label='Confirm password', widget=forms.PasswordInput)

    class Meta:
        model = User
        fields = ('username', 'full_name', 'email', 'organization', 'phone',)

    def clean_email(self):
        email = self.cleaned_data.get('email')
        qs = User.objects.filter(email=email)
        if qs.exists():
            raise forms.ValidationError("email is taken")
        return email

    def clean_password2(self):
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2


class UserAdminCreationForm(forms.ModelForm):
    """A form for creating new users. Includes all the required
    fields, plus a repeated password."""
    password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
    password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)

    class Meta:
        model = User
        fields = ('username', 'full_name', 'email', 'organization', 'phone',)

    def clean_password2(self):
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super(UserAdminCreationForm, self).save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user


class UserAdminChangeForm(forms.ModelForm):
    """A form for updating users. Includes all the fields on
    the user, but replaces the password field with admin's
    password hash display field.
    """
    password = ReadOnlyPasswordHashField()

    class Meta:
        model = User
        fields = ('username', 'password', 'email', 'organization', 'phone', 'is_active', 'is_superuser',)

    def clean_password(self):
        # Regardless of what the user provides, return the initial value.
        # This is done here, rather than on the field, because the
        # field does not have access to the initial value
        return self.initial["password"]

Honestly I have no idea what will show you up to.. Maybe I have to show you my admin.py too? anyways,

Here is my vue.js SignUp axios.

data: () => ({
  username: '',
  password: '',
  re_pass: '',
  full_name: '',
  email: '',
  organization: '',
  phone: ''
}),
sign_up () {
      this.$validator.validateAll()
      if (this.password !== this.re_pass) {
        alert('check the password')
        document.getElementById('re_password').focus()
      } else {
        let axios = this.$axios

        let formData = new FormData()
        formData.append('username', this.username)
        formData.append('password', this.password)
        formData.append('full_name', this.full_name)
        formData.append('email', this.email)
        formData.append('organization', this.organization)
        formData.append('phone', this.phone)
        const baseURI = 'http://127.0.0.1:8000' //my localhost api
        const config = {
          headers: {
            'Content-Type': 'application/json'
          }
        }

        /* Do axios post */
        axios.post(`${baseURI}/users/`, formData, config)
          .then((response) => {
            alert('회원가입 되었습니다. (You Signed Up.)')
            this.$router.push({
              name: 'sign_in'
            })
          })
          .catch((e) => {
            console.error(e)
            alert('전송 중 문제가 발생하였습니다. 다시시도 해주세요. (The error accurred. Please try to do again.)')
          })
      }
    }

Actually it works but I hope it would be worked more naturally with automatic hashed password with default set up as Django model.

Sorry about that complicated question. Honestly I posted first time in this site and I'm not English speaker too. I really struggled to find some example Django with Vue.js, and custom user model so..

Thanks to read that all and Don't hesitate to tell me.

Have a good day!

++ Add my serializers.py too ./Users/serializers.py

from rest_framework import serializers
from .models import User

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        # Model set
        model = User
        # Field set
        fields = ('id', 'username', 'email', 'organization', 'full_name', 'phone', 'password',
                  'is_superuser', 'is_active', 'is_staff', 'last_login', 'created_date', 'updated_date')

And my views. ./Users/views.py

from rest_framework import viewsets
from .serializers import UserSerializer
from .models import User


class UsersViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

Project url. ./Project_name/urls.py

from django.contrib import admin
from django.urls import path, include
from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token
from rest_framework import routers

from Users.views import UsersViewSet

router = routers.DefaultRouter()
router.register('users', UsersViewSet)

urlpatterns = [
    # Normal urls
    path('admin/', admin.site.urls),
    path('', include(router.urls)),

    # JWT auth
    path('api-token-auth/', obtain_jwt_token),
    path('api-token-refresh/', refresh_jwt_token),
]

Thanks again.

kofi_codes
  • 48
  • 1
  • 5
  • Why are you using form instead of serializer? – some_code Nov 09 '18 at 07:25
  • @some_code I'm using serializer but I couldn't find a example what is fit my environment. i will add my serializer in this post too! – Dong-geun Jayse Ryu Nov 09 '18 at 07:29
  • That's seems to be the problem, your form uses `user.set_password(self.cleaned_data["password1"])` but your API endpoint uses the serializer to save the object, which does not have that piece of code. – dethos Nov 09 '18 at 08:06
  • @dethos Then I have to choice between form and serializer? and also serializer will be more wise one? – Dong-geun Jayse Ryu Nov 09 '18 at 08:09
  • Your `viewset` does not use the form but the serializer, so you should override the `save()` method of the serializer as well. – dethos Nov 09 '18 at 08:12
  • @dethos ahh okay. so i will add 'save()' method in serializer right? Thanks :D i will try it! – Dong-geun Jayse Ryu Nov 09 '18 at 08:17
  • If it works, let me know and I will turn it into a definitive answer. – dethos Nov 09 '18 at 08:30
  • @dethos Okay i got it. honestly i didn't know i can do something in serializer so i have to learn first and then next, try to figure out. Thank you :) – Dong-geun Jayse Ryu Nov 09 '18 at 08:41
  • @dethos Hey, I figured out as serializers.py - 'save()'! even though i found the solution in other question here https://stackoverflow.com/questions/27586095/why-isnt-my-django-user-models-password-hashed/27586289 however you gave me a clue for this! thank you very much :D – Dong-geun Jayse Ryu Nov 12 '18 at 08:01

1 Answers1

0

@dethos He gave me a clue for this.

So finally I found a solution here

Why isn't my Django User Model's Password Hashed?

And I will share my edited serializers.py

from rest_framework import serializers
from .models import User


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        # Model set
        model = User
        # Field set
        fields = ('id', 'username', 'email', 'organization', 'full_name', 'phone', 'password',
                  'is_superuser', 'is_active', 'is_staff', 'last_login', 'created_date', 'updated_date')

    def create(self, validated_data):
        password = validated_data.pop('password', None)
        is_active = validated_data.pop('is_active', True)
        instance = self.Meta.model(**validated_data)
        if password is not None:
            instance.set_password(password)
        instance.save()
        return instance

    def update(self, instance, validated_data):
        for attr, value in validated_data.items():
            if attr == 'password':
                instance.set_password(value)
            else:
                setattr(instance, attr, value)
        instance.save()
        return instance

I added 'set_password', and 'is_active''s default value.