How to create model objects that belong to unique user - django

I am setting up a delivery app .I want to be able to create a Reciever that belongs to the user logged in when created.
class CustomUser(AbstractUser):
first_name = models.CharField(max_length=30, blank=False)
last_name = models.CharField(max_length=30, blank=False)
email = models.EmailField(blank=False)
class Reciever (models.Model):
name = models.CharField(max_length=256,blank=False)
I want to create a Reciever object that belongs to a particular user.
When i create a Reciever now , it is available to all users.

In that case you can use a OneToOneField [Django-doc], which is a ForeignKey [Django-doc], but with a uniqueness constraint attached to it, like:
class Reciever(models.Model):
name = models.CharField(max_length=256,blank=False)
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
In the view, you can thus attach the logged in user to the Receiver, like:
def some_view(request):
Receiver.objects.create(name='foo', user=request.user)
given of course you already made CustomUser your customer model [Django-doc].
If you made a ModelForm, you can exclude the user field, and set it in the view, for example:
def some_view(request):
form = ReceiverForm(request.POST)
if form.is_valid():
object = form.save(commit=False)
object.user = request.user
object.save()
# ...
# ...

Related

How to predefine value inside model/form in Django?

I'm creating simple app which allows users to create group.
When user create group it has following fields:
name
desc
inviteKey - i would this field to be hidden and generate 10 characters code and then send it.
My models:
class Group(models.Model):
groupName = models.CharField(max_length=100)
description = models.CharField(max_length=255)
inviteKey = models.CharField(max_length=255)
class Members(models.Model):
userId = models.ForeignKey(User, on_delete=models.CASCADE)
groupId = models.ForeignKey(Group, on_delete=models.CASCADE)
isAdmin = models.BooleanField(default=False)
Form:
class GroupForm(forms.ModelForm):
groupName = forms.CharField(label='Nazwa grupy', max_length=100)
description = forms.CharField(label='Opis', max_length=255)
inviteKey: forms.CharField(label='Kod wstępu')
class Meta:
model = Group
fields = ['groupName', 'description', 'inviteKey' ]
View:
def createGroup(request):
if request.method == "POST":
form = GroupForm(request.POST)
if form.is_valid():
form.save()
messages.success(request, f'Group created')
return redirect('/')
else:
inviteKey = generateInviteKey()
form = GroupForm(initial={'inviteKey': inviteKey})
return render(request, 'group/createGroup.html',{'form': form})
Now i have form and inviteKey is visible and editable. I want this key to be visible but not editable.
The best way to do that my opinion is to set the default value for your invitation key in your model, that way, token is created "in the background" with a unique key, but we can go further.
For example :
import uuid
token = models.UUIDField(
default=uuid.uuid4,
unique=True,
editable=False,
)
This way you are sure that the token is unique (UUID is unique by design, but still) you cannot edit it so no wrong token can occur and last of all each object will get a unique token with no work on your side.
I am using UUID because it is recommended by Django as per the Documentation for token and unique identifier.
Note : If you set the UUID with a default value, you cannot get it before the object is created, depending on your use you might want to set it in the form (see answer below).
You can make the field disabled, so:
class GroupForm(forms.ModelForm):
groupName = forms.CharField(label='Nazwa grupy', max_length=100)
description = forms.CharField(label='Opis', max_length=255)
inviteKey = forms.CharField(label='Kod wstępu', disabled=True)
class Meta:
model = Group
fields = ['groupName', 'description', 'inviteKey' ]
This will also prevent a user from fabricating a POST request that contains a different invite key.
A problem that one now has to solve however is that we do not want to generate a different inviteKey when the user submits the form. This can be handled with session data, although it is not a very elegant solution. In that case we thus change the view to:
def createGroup(request):
if request.method == 'POST' and 'inviteKey' in request.session:
inviteKey = request.session['inviteKey']
form = GroupForm(request.POST, initial={'inviteKey': inviteKey})
if form.is_valid():
form.save()
messages.success(request, f'Group created')
return redirect('/')
else:
request.session['inviteKey'] = inviteKey = generateInviteKey()
form = GroupForm(initial={'inviteKey': inviteKey})
return render(request, 'group/createGroup.html',{'form': form})
You probably alo might want to make your inviteKey field unique, to prevent creating multiple groups with the same inviteKey:
class Group(models.Model):
groupName = models.CharField(max_length=100)
description = models.CharField(max_length=255)
inviteKey = models.CharField(max_length=255, unique=True)

How to filter post objects in django according to a particular user model field?

I'm currently creating a Social platform with Django. Right now, I'm developing homepage and want to show posts matching a userfield.
This is my Post model:
class Discussion(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Account, on_delete=models.CASCADE)
post = models.TextField()
date = models.DateTimeField(default=timezone.now)
This is user account model:
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name='email', max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
classid = models.CharField(max_length=10)
This is view:
#login_required(login_url='login')
def home(request):
if request.method == 'GET':
discussions = Discussion.objects.get(post=Account.classid).order_by('-date')
form = PostDiscussionForm()
return render(request, 'app/home.html', {'discussions':discussions,'form':form})
else:
form = PostDiscussionForm(request.POST)
newdisscussion = form.save(commit=False)
newdisscussion.author = request.user
newdisscussion.save()
return redirect('home')
I want to show only logged users matching their classid (from user account model)
What I gather from here, is that you want to show which users are currently online/logged in? For that you'll need to store additional information when logging like, add a boolean field in the user model indicating whether or not the user has logged in and then filter based on that field.
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name='email', max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
logged_in = models.BooleanField(default=False)
classid = models.CharField(max_length=10)
Now add additional logic which sets this field to true when the user eventually logs in from your /login route.
Then Filter using:
$ return Account.objects.filter(logged_in==True)
This will return you all users that have currently logged in, if you're not using any serializers, than use value_list for getting the values only rather than the whole user objects.
$ return Account.objects.filter(logged_in==True).values_list('classid', flat=True)

Create a separate user type

I am working on an intranet web application which needs two types of users. Normal users that can be setup from django admin and specific type of users -
Employees.
I have the following model for Employee type user.
class Employee(models.Model):
emp_name = models.CharField(max_length=500)
slug = models.SlugField(unique=True, default='')
location = models.CharField(max_length=200)
email = models.EmailField()
experience = models.TextField(blank=True)
primary_skill = models.ManyToManyField(PrimarySkill)
secondary_skill = models.ManyToManyField(SecondarySkill)
I tried having a OneToOneField like this as per the official doc and
this article:
user = models.OneToOneField(User, blank=True, null=True, on_delete=models.CASCADE)
#receiver(post_save, sender=User)
def create_employee(sender, instance, created, **kwargs):
if created:
Employee.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_employee(sender, instance, **kwargs):
instance.employee.save()
I realized that this is the opposite of what I want. Every time a User
is created from the admin, there was an entry created in the
app_employee table.
What I want is this:
Every time an Employee is created, I need a User created.
An Employee can be created using a separate signup form, say emp_signup
How do I approach this scenario?
I have achieved this using a custom user based on AbstractUser inspired by this article.
class CustomUser(AbstractUser):
pass
class Employee(CustomUser):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
# other fields
In settings.py, I then add the following key:
AUTH_USER_MODEL = 'myapp.CustomUser'
And wherever I need to refer the User class, I use get_user_model(), which will substitute our custom user, in views and forms as follows:
views.py
from django.contrib.auth import get_user_model
def user_profile(request):
User = get_user_model()
user = get_object_or_404(User, username=request.user.username)
return render(request, 'user/user_profile.html', {
'site_user': user
})
forms.py
class SignUpForm(UserCreationForm):
class Meta:
model = get_user_model()
fields = ('username', 'email', 'password1', 'password2',)

Django rest framework : extend user model for customer - one to one field

I have a customer model in Bcustomer app that extends the django User model, So I will save the basic details such as name in User table and the remaining data (city, etc) in customer table.
When I call the below code through API, it shows the following error. But data is saving in the tables. I also want to implement the get and put calls for this api.
Got AttributeError when attempting to get a value for field `city` on serializer `CustomerSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `User` instance.
Original exception text was: 'User' object has no attribute 'city'.
my Bcustomer/models.py
class BCustomer(models.Model):
customer = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True, blank=True )
address = models.CharField(max_length=50)
city = models.CharField(max_length=256)
state = models.CharField(max_length=50)
user = models.ForeignKey(settings.AUTH_USER_MODEL, db_index=True, on_delete=models.CASCADE, related_name='customer_creator')
# more fields to go
def __str__(self):
# return str(self.name) (This should print first and last name in User model)
class Meta:
app_label = 'bcustomer'
my Bcustomer/serializers.py
from django.contrib.auth import get_user_model
from models import BCustomer
class CustomerSerializer(serializers.HyperlinkedModelSerializer):
city = serializers.CharField()
class Meta:
model = get_user_model()
fields = ('first_name', 'email','city')
def create(self, validated_data):
userModel = get_user_model()
email = validated_data.pop('email', None)
first_name = validated_data.pop('first_name', None)
city = validated_data.pop('city', None)
request = self.context.get('request')
creator = request.user
user = userModel.objects.create(
first_name=first_name,
email=email,
# etc ...
)
customer = BCustomer.objects.create(
customer=user,
city=city,
user=creator
# etc ...
)
return user
my Bcustomer/views.py
class CustomerViewSet(viewsets.ModelViewSet):
customer_photo_thumb = BCustomer.get_thumbnail_url
permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
queryset = BCustomer.objects.all()
serializer_class = CustomerSerializer
my Bcustomer/urls.py
router.register(r'customer', views.CustomerViewSet, 'customers')
POST request format
{
"first_name":"Jsanefvf dss",
"city":"My City",
"email":"myemail#gmail.com",
#more fields
}
I also need to implement put and get for this api. Now data is saving in both tables but shows the error.
Sure it complains.
Validation goes well, so does the creation but once it's created, the view will deserialize the result and return it to the client.
This is the point where it goes south. Serializer is configured to think that city is a field of the default user while it actually is part of BCustomer. In order to work this around, you should set the source argument to the city field. You might need to update the serializer's create/update to reflect that change, not sure about that one.

How to use a submit a one-to-many form

I have a list of employees who work at a site. Each site is owned by a User (using Django's standard user model).
I want to create a form that adds an employee and automatically links them to a site dependent on who the authenticated user is:
models.py:
class Employee(models.Model):
site = models.ForeignKey(Site, null=True)
employee_name = models.CharField(default='name', max_length=128, blank=False, null=False)
class Site(models.Model):
user = models.ForeignKey(User)
site_name = models.CharField(max_length=128, blank=False, null=False)
views.py:
site_profile = Site.objects.get(user=request.user)
if request.method == "POST":
form = EmployeeAddForm( request.POST )
if form.is_valid():
obj = form.save(commit=False)
obj.site = site_profile
obj.save()
return redirect('dashboard_home')
form = EmployeeAddForm()
return render(request, "dashboard/employees.html", {'form': form })
forms.py:
class EmployeeAddForm(forms.ModelForm):
class Meta:
model = Employee
fields = ( 'employee_name')
This code will add the employee to the database, but in django admin, list_display = 'site' results in Site object not the actual site name. It does not appear that the employee is linked to the site.
If I use obj.site = site_profile.id (adding .id), I get the error Cannot assign "1": "Employee.site" must be a "Site" instance.
Found the error: the above code is correct, I simply had a tab ordering error in my Site modeL
class Site(models.Model):
...
def __str__(self):
return self.site_name
def should have been inserted 1 tab inwards.