Updating data on linked models - python-3.x

Here's my user model,
class User(AbstractBaseUser, PermissionsMixin, Base):
user_id = models.AutoField(primary_key=True)
email = models.EmailField(db_index=True, max_length=100, unique=True)
is_advisor = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
And here's the user profile
class UserProfile(Base):
profile_id = models.AutoField(primary_key=True)
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user_profile')
first_name = models.CharField(null=True, blank=True, max_length=100)
last_name = models.CharField(null=True, blank=True, max_length=100)
thumbnail = models.ImageField()
Here's the routers,
router.register(r'user', UserViewSet),
router.register(r'user/profile', UserProfileViewSet, basename='UserProfile')
What will be the path to update the Profile for a specific user, say user_id 3. I'm new to django.

It depends on the lookup_field that you will set on the view UserProfileViewSet which as documented:
lookup_field - The model field that should be used to for performing object lookup of individual model instances
If you want to update it based on the related field UserProfile.user using the primary key of the other model User.user_id, then:
class UserProfileViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
lookup_field = 'user' # Or "user_id"
URL path would be:
http://127.0.0.1:8000/my_app/user/profile/123/
Where 123 is a user_id of a user.
If you want to update it based on the related field UserProfile.user but using another field on the related table e.g. User.username (just for the sake of example, let's say it is in your User model and is unique)
class UserProfileViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
lookup_field = 'user__username'
URL path would be:
http://127.0.0.1:8000/my_app/user/profile/john_lennon/
Where john_lennon is a username of a user.
For your consideration. If you want the id to be consistent between User and UserProfile in a way that user_id is just equivalent to profile_id, you might consider just making it the primary key of your UserProfile
class UserProfile(Base):
profile_id = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user_profile', primary_key=True)
...
That way, your lookup_field can just be profile_id as it is just equivalent to user_id.

Related

ensure one type of user cannot log in as another type of user django

In Django, how can I make sure that one type of user cannot log in as another type of user? For example, if there are two types of users on my website, teachers and students, teachers should not be able to use their credentials to log in as a student and vice versa.
class Teacher(models.Model):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
name = models.CharField(max_length=200, default="")
email = models.CharField(max_length=200, unique=True)
class Student(models.Model):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
name = models.CharField(max_length=200, default="")
email = models.CharField(max_length=200, unique=True)
you can achieve it by using user_passes_test
let us say only students should access the registration page, so you can create user_passes_function which returns True if user is student otherwise False and decorate the studen_registation view as shown in the snippet code below
from django.contrib.auth.decorators import user_passes_test
from django.shortcuts import get_object_or_404
def student_check(user):
try:
get_object_or_404(Student, user=user)
except:
return False
else:
return True
#user_passes_test(student_check)
def student_registration(request):

How to custom a search filter in django rest framework

Model
class Student(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
first_name = models.CharField(verbose_name=_('first name'), max_length=20, blank=True, null=True)
last_name =models.CharField(verbose_name=_('last name'), max_length=20, blank=True, null=True)
class Bicycle(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
name = models.CharField(verbose_name=_('bicycle name'), max_length=20, blank=True, null=True)
student_id = models.UUIDField(default=uuid4)
View:
class BicycleList(AdUpdateMixin, AdDestroyMixin, AdListMixin, AdCreateMixin, generics.GenericAPIView):
permission_classes = [IsAuthenticated]
queryset = Bicycle.objects.all()
search_fields = ['name', 'id', 'student_id']
Now, I want create a custom search field in django rest framework. So that I can search first_name and last_name in Bicycle List ?
If you can use student as a foreign key in Bicyle Model then you can query as:
from django.db.models import Q
Bicycle.objects.filter(
Q(student__first_name__icontains=t)
| Q(student__first_name__icontains=t)
)

Custom User Model with multiple Unique id - Django RestFramework

Hi StackOverFlow buddies,
I have created a custom User model for my project and would like to register the Users with Restframework.
I want Custom User model to have 2 unique fields together, for which I followed "Official doc" for unique_together property. It seems to be only taking only 1 field (ie email for my case), as a unique one.
Relevant piece my code until this point looks like this:
PS: Let me know if more info is required.
models.py
class MasterUser(AbstractBaseUser):
email = models.EmailField(verbose_name='email address',max_length=255,unique=True,)
firstname = models.CharField(max_length=100,blank=True)
contact = models.CharField(max_length=100, blank=True)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['firstname']
class Meta:
unique_together = (('email', 'contact'),)
serializer.py
class RegisterUserSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(style={'input_type': 'password'}, write_only= True)
class Meta:
model = MasterUser
fields = ('firstname', 'password', 'password2', 'email','contact')
extra_kwargs = {
'password': {'write_only': True},
}
def save(self):
account = MasterUser(
email = self.validated_data['email'],
firstname = self.validated_data['firstname'],
contact = self.validated_data['contact'],
)
password = self.validated_data['password']
password2 = self.validated_data['password2']
if password != password2:
raise serializers.ValidationError({'password': 'Password doesnt matches'})
account.set_password(password)
account.save()
return account
views.py
#api_view(['POST'])
def registration_view(request):
if request.method == "POST":
serializer = RegisterUserSerializer(data= request.data)
data = {}
if serializer.is_valid():
account = serializer.save()
data['response'] = "Successfully registered new user!"
else:
data = serializer.errors
return Response(data)
Where am I missing to implement thing?
unique_together means the set of fields you defined should be unique for all Users. If you also want contact to be a unique field when stored in db, you should pass the unique parameter like so:
class MasterUser(AbstractBaseUser):
...
contact = models.CharField(max_length=100, blank=True, unique=True)
...

Why is the password stored in a raw format to the database in Django?

I have this Serializer:
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
fields = '__all__'
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
name = validated_data['name'],
id = validated_data['id'],
user = Person.objects.create(name=name,
password=validated_data['password'],
id=id)
return user
I thought that passwords are automatically saved as hashs in the database but with this serializer the password is saved in a raw format. How could I modify this to save the hash, not the raw version?
Edit. Here is my Person model and my UserManager:
class UserManager(BaseUserManager):
def create_user(self, name, password):
user.set_password(password)
user.save(using=self._db)
return user
class Person(AbstractBaseUser):
id = models.CharField(primary_key=True, unique=True, blank=False, max_length=100)
name = models.CharField(max_length=100, unique=True, null=False, blank=False)
USERNAME_FIELD = 'name'
REQUIRED_FIELDS = []
objects = UserManager()
As you can see, the name is here used as the username. Where do you define the password hashing, in serializer or in my models.py?
You should set the password like this.
user = Person(name=name, id=id)
user.set_password(validated_data['password'])
user.save()
set_password method takes care of hashing.

how to convert this model into non editable model in admin of django?

I create a model but I need a non-editable model. When I visit the model it's just displaying text, not giving any option of update the record in admin panel.
model.py
class Contact(models.Model):
name = models.CharField(max_length=160)
email = models.EmailField()
subject = models.CharField(max_length=255)
message = models.TextField()
def __str__(self):
return self.subject
While searching I get information about readonly but I do not still understand how to use it.
There are two ways to do this. One, make editable=False for all the fields, then it will not be edible anywhere(modelform and adminsite):
class Contact(models.Model):
name = models.CharField(max_length=160, editable=False)
email = models.EmailField(editable=False)
subject = models.CharField(max_length=255, editable=False)
message = models.TextField(editable=False)
Two, in adminsite, use readonly_fields:
class ContactAdmin(admin.ModelAdmin):
readonly_fields = ('name', 'email', 'subject', 'message')
admin.site.register(Contact, ContactAdmin)

Resources