I'm trying to implement class Based views with permissions and they do not seem to connect, although I believe I strictly followed Django User's guide.
First: set up of a Custom User model, based on Proxies, in accounts.models
class CustomUser(AbstractUser):
some_fields...
Then, I created a Manager:
class EmployeeManager(models.Manager):
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).filter(status_type=CustomUser.StatusType.EMPLOYEE)
Followed by the type of profile:
class Employee(CustomUser):
objects = EmployeeManager()
class Meta:
proxy = True
permissions = [("communities.view_region", "Can view region")]
Where I set a permission, make the migrations and migrate.
After, I create the view:
import communities.models as comms
class RegionsListView(ListView):
model = comms.Region
Then, configuration of the url and its view:
rom django.urls import path, include
import communities.views as views
from django.contrib.auth.decorators import permission_required
app_name = 'communities'
urlpatterns = [
path("regions/list/", permission_required("communities.view_region")(views.RegionsListView.as_view()))
Then I log in as an employee and I get an error 403 when calling this url.
What did I miss ?
Remarks:
using permission_required = 'communities.view_region' in the view.py file produces the same result.
when logging as a superuser, I get of course the right page.
In order to implement the permissions, it had to be done programmatically, while creating a new user.
In the workflow,
the new User gets an activation e-mail with a unique link,
the click gives him access to the website and he is required to immediately change his password,
the validation implements the permission (in this case, a Group)
Here is the code.
from django.contrib import messages
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.forms import PasswordChangeForm
from django.shortcuts import redirect, render
from django.contrib.auth.models import Group
#login_required
def change_password(request):
if request.method == "POST":
form = PasswordChangeForm(user=request.user, data=request.POST)
if form.is_valid():
user = form.save()
# Attributes to a user a group depending on his status.
user.groups.add(Group.objects.get(name=user.status_type))
update_session_auth_hash(request, user) # Important!
messages.success(request, 'Your PWD has been changed')
return redirect('change_password')
else:
messages.error(request, 'Please Correct the Error')
else:
form = PasswordChangeForm(request.user)
return render(request, 'accounts/change_password.html', {'form': form})
Related
I'm trying to update my Profile model with some data that I get from a form, but I get this error
name 'get_object_or_404' is not defined
Here's my code for the view (It's pretty basic at this point)
from django.shortcuts import render
from django.contrib import messages
from django.contrib.auth.models import User
from users import models
from users.models import Profile
from .forms import WeightForm
# Create your views here.
def home(request):
profile = get_object_or_404(pk=id)
form = WeightForm(request.POST, instance=profile)
if form.is_valid():
form.save
return render(request, 'Landing/index.html',{'form':form})
You need to import it. Furthermore in a get_object_or_404(…) [Django-doc], you first specify a model class or a queryset, so in this case get_object_or_404(Profile, …):
from django.shortcuts import get_object_or_404
def home(request):
profile = get_object_or_404(Profile, pk=id)
form = WeightForm(request.POST, instance=profile)
if form.is_valid():
form.save()
return render(request, 'Landing/index.html',{'form':form})
Note: In case of a successful POST request, you should make a redirect
[Django-doc]
to implement the Post/Redirect/Get pattern [wiki].
This avoids that you make the same POST request when the user refreshes the
browser.
I have an application where a user can submit a form which goes into the database (POSTGRES).
I want to be able to automatically send the username of the user logged in to the same database, so i can keep track of who is submitting. (I do not want to put a form line with the username, i want this to be dealt with in the back-end).
what I managed to do is get the user-id, but it stays null, and I do not know how to get the username in the database and to complete it at each submission.
I hope I am clear,
thanls guys.
Here is my code
models.py
from django.db import models as db_models
from django.contrib.auth.models import User
from django.contrib.gis.db import models
class Fertidb(models.Model):
user = db_models.ManytoManyField(User, on_delete=models.CASCADE)
area = models.IntegerField()
plot = models.FileField(upload_to='KML_FILES', blank=True)
def __str__(self):
return f' Parcelles de {self.user.username}'
forms.py
from django import forms
from django.contrib.auth.models import User
from .models import Fertidb
class FertidbForm(forms.ModelForm):
class Meta:
model = Fertidb
labels = {
"plot": "Importez votre fichier KML"
}
fields = ['culture', 'area', 'plot']
views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from .forms import FertidbForm
from django.contrib.auth.models import User
title = 'FERTISAT'
#login_required
def fertisatmap(request):
mapbox_access_token = 'pk.eyJ1IjoiaGFtemFiIiwiYSI6ImNrMHdwYmQ2bzA2OGYzbHB1Z292eGxneDgifQ.rGPQjaoWuOdnq_UdxAfQ_w'
if request.method == "POST":
o_form = FertidbForm(request.POST, request.FILES)
if o_form.is_valid():
o_form.save(commit=False)
o_form.user = request.user.username()
messages.success(request, f'Vos informations ont été envoyées')
return redirect('fertisat-map')
else:
o_form = FertidbForm()
context = {'title': title, 'o_form': o_form}
return render(request, 'fertisat/fertisatmap.html ', context, {'mapbox_access_token': mapbox_access_token})
Try to update your view like so:
from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from .forms import FertidbForm
from django.contrib.auth.models import User
title = 'FERTISAT'
#login_required
def fertisatmap(request):
mapbox_access_token = 'pk.eyJ1IjoiaGFtemFiIiwiYSI6ImNrMHdwYmQ2bzA2OGYzbHB1Z292eGxneDgifQ.rGPQjaoWuOdnq_UdxAfQ_w'
if request.method == "POST":
o_form = FertidbForm(request.POST, request.FILES)
if o_form.is_valid():
fertidb = o_form.save(commit=False)
fertidb.user = request.user
fertidb.save()
messages.success(request, f'Vos informations ont été envoyées')
return redirect('fertisat-map')
else:
o_form = FertidbForm()
context = {'title': title, 'o_form': o_form}
return render(request, 'fertisat/fertisatmap.html ', context, {'mapbox_access_token': mapbox_access_token})
(commit=False) use for creating the model instance without submit to database, then assign current user to your new model instance fertidb.user = request.user and then call .save() to commit your data to database
Btw, mapbox_access_token suppose to stay inside settings.py in case you want to load it from environment variable when deploy production. like so:
settings.py
MAPBOX_ACCESS_TOKEN="pk.eyJ1IjoiaGFtemFiIiwiYSI6ImNrMHdwYmQ2bzA2OGYzbHB1Z292eGxneDgifQ.rGPQjaoWuOdnq_UdxAfQ_w"
views.py
from django.conf import settings
...
def fertisatmap(request):
mapbox_access_token = settings.MAPBOX_ACCESS_TOKEN
Hope that helps!
There are two issues here:
1. In your Model, you want a User, but in your form, you are assigning it the username, which I think is a string.
user = db_models.ManytoManyField(User, on_delete=models.CASCADE)
and
o_form.user = request.user.username()
Just change the second line to o_form.user = request.user.
2. You are not saving the user anyway.
You have to save your model again after you assign the user.
Thanks fo the help guys.
#Toan Quoc Ho thank you I made the modifications but I still have a problem.
The database displays the user_id, but I would like it to display the username.
I guess my problem is in the model file. How do I modify the following, so I get the username in the database.
user=deb_models.ForeignKey(User,on_delete)models.CASCADE) puts the user_id -> I would like to have the username. How do I call it ?
models.py
*from django.db import models as db_models
from django.contrib.auth.models import User
from django.contrib.gis.db import models
class Fertidb(models.Model):
user = db_models.ForeignKey(User, on_delete=models.CASCADE)
culture = models.CharField(max_length=50)
area = models.IntegerField()
plot = models.FileField(upload_to='KML_FILES', blank=True)
def __str__(self):
return f' Parcelles de {self.user.username}'*
I'm trying to evaluate a situation here. I have a custom user Model (Using AbstractUser) that contains an extra field called 'role'. I can access the value of this field in views.py using a variable. Now I want to use that variable to check for a condition in signals.py before executing post_save.
Here are my code snippets so far.
views.py
from django.shortcuts import render, redirect
from .forms import UserRegisterForm, UserUpdateForm, ProfileUpdateForm
from django.contrib import messages
from django.contrib.auth.decorators import user_passes_test, login_required
# Create your views here.
#user_passes_test(lambda u: u.is_superuser)
def register(request):
if request.method == 'POST':
register_form = UserRegisterForm(request.POST)
if register_form.is_valid():
register_form.save()
username = register_form.cleaned_data.get('username')
role = register_form.cleaned_data.get('role') //**This is the variable**
messages.success(request, f'Welcome {username}, thank you for signing up.')
return redirect('home')
else:
register_form = UserRegisterForm()
context = {
'register_form' : register_form,
'title' : 'Register'
}
return render(request, 'users/register.html', context)
signals.py
from django.db.models.signals import post_save
from django.conf import settings
from django.contrib.auth import get_user_model
from django.dispatch import receiver
from .models import Teacher
User = get_user_model()
#receiver(post_save, sender=User)
def create_teacher(sender, instance, created, **kwargs):
if created:
if hasattr(instance, 'role') and role == 'teacher': //Here is the condition
Teacher.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_teacher(sender, instance, **kwargs):
instance.teacher.save()
Can someone please help me on how to go about it? Thanks
You make this much harder than it needs to be. Just create the teacher in the view.
if register_form.is_valid():
instance = register_form.save()
username = register_form.cleaned_data.get('username')
role = register_form.cleaned_data.get('role')
if role == 'teacher':
Teacher.objects.create(user=instance)
messages.success(request, f'Welcome {username}, thank you for signing up.')
return redirect('home')
learning django and created a new project.
login to admin page through http://127.0.0.1:8000/admin
But after adding profile code for every user I am getting the below error
Error
user/models.py
from django.db import models
from django.contrib.auth.models import User
from PIL import Image
class Profile(models.Model):
user=models.OneToOneField(User,on_delete=models.CASCADE)
image=models.ImageField(default='default.jpg',upload_to='profile_pics')
def __str__(self):
return f'{self.user.username} Profile'
def save(self):
super().save()
img=Image.open(self.image.path)
if img.height>300 or img.width >300:
output_size=(300,300)
img.thumbnail(output_size)
img.save(self.image.path)
user/views.py
from django.shortcuts import render,redirect
from django.contrib.auth.forms import UserCreationForm
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from .forms import UserRegisterForm,UserUpdateForm,ProfileUpdateForm
def register(request):
if request.method=="POST":
form=UserRegisterForm(request.POST)
if form.is_valid():
form.save() # it will save user in database
#form.cleaned_data is dictionary it will contain the data from form
username=form.cleaned_data.get('username')
messages.success(request,f'Your account has been created!')
return redirect('login')
else:
form=UserRegisterForm()
# instance of UserCreationForm() class will create form in template as we are passing there
return render(request,'users/register.html',{'form':form})
#login_required()
def profile(request):
if request.method == "POST":
u_form=UserUpdateForm(request.POST,instance=request.user)
p_form=ProfileUpdateForm(request.POST,request.FILES,instance=request.user.profile)
if u_form.is_valid() and p_form.is_valid():
u_form.save()
p_form.save()
messages.success(request, f'Your account has been updated!')
return redirect('profile')
else:
u_form = UserUpdateForm(instance=request.user)
p_form = ProfileUpdateForm(instance=request.user.profile)
context={
'u_form':u_form,
'p_form':p_form
}
return render(request,'users/profile.html',context)
users/forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class UserRegisterForm(UserCreationForm):
email=forms.EmailField()
class Meta:
model=User #model will affected will be User as after submitting form user table gonna affect. and below will fields in forms
fields=['username','email','password1','password2']
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User # model will affected will be User as after submitting form user table gonna affect. and below will fields in forms
fields = ['username', 'email']
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model=Profile
fields=['image']
users/signals.py
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
#receiver(post_save,sender=User)
def create_profile(sender,instance,created,**kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_profile(sender, instance, created, **kwargs):
instance.profile.save()
it might be that the Admin profile is not available and it is not creating.any suggestions ?
You need to create an admin profile first. Since the admin has no profile, it's showing related objects doesn't exist. I faced a similar situation and I created an admin profile. It worked
-Enter a python terminal in your virtual environment
python manage.py shell
-Run this, will fix your issue without blowing out the database
from django.contrib.auth.models import User
from users.models import Profile
user = User.objects.get(username='enter_admin_user_here')
profile = Profile(user=user)
profile.save()
This will add a user profile for the admin user.
I had the same problem and none of the other answers here helped. This is what did the trick:
go to users/apps.py ------
if your code editor underlines apps with a red line, then click on it , it will request you to install package django-email-users, click the request it will then download if it doesn't automatically request you can research how to install django- email-users
else --- go to step 2
delete all your migrations files like(001, 002 and so on) in every app excluding the
--init--.py (or you will have another problem).
delete your media pictures
delete db.sqlite
run migrate, migrations, migrate, createsuperuser in that order
now if it shows other error that has to do with file path, try to put back your original
media pictures and repeat steps 2-6 again,
Finally for me I found an answer!
in the setting
installed apps
I register users as 'users' but when i change it to 'users.apps.UsersConfig', it work!
Remove this Ready function from users - app.py file and try
def ready(self):
import users.signals
Check if your users/app.py file is as follows:
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals
If it is not as above, then you have not imported the signals.py script .
Add this line of code to your users/__init__.py
default_app_config ='users.apps.UsersConfig'
It would be easier if you delete your db.sqlite3 from your workspace and create new superuser and make migrations also migrate. It worked in my code
I am building a small application which needs user profiles, I've used the build in user system from Django. But I have a problem regarding that even if you are not logged in you can still view the profile also another thing is that each user should only see his profile not others I need some tips on this
views.py
class UserProfileDetailView(DetailView):
model = get_user_model()
slug_field = "username"
template_name = "user_detail.html"
def get_object(self, queryset=None):
user = super(UserProfileDetailView, self).get_object(queryset)
UserProfile.objects.get_or_create(user=user)
return user
class UserProfileEditView(UpdateView):
model = UserProfile
form_class = UserProfileForm
template_name = "edit_profile.html"
def get_object(self, queryset=None):
return UserProfile.objects.get_or_create(user=self.request.user)[0]
def get_success_url(self):
return reverse("profile", kwargs={"slug": self.request.user})
Since you are using the Class Based Generic View, you need to add decorator #login_required in your urls.py
#urls.py
from django.contrib.auth.decorators import login_required
from app_name import views
url(r'^test/$', login_required(views.UserProfileDetailView.as_view()), name='test'),
Have you checked out the login_required decorator? Docs are here.
Since it seems you are using Class Based Views, you need to decorate in the urlconf, see here for more info.
At this moment you can add LoginRequiredMixin for your custom view.
Example:
class MyListView(LoginRequiredMixin, ListView): # LoginRequiredMixin MUST BE FIRST
pass
Doc: https://docs.djangoproject.com/en/4.1/topics/auth/default/#the-loginrequiredmixin-mixin
The below is what you should typically do
#login_required
def my_view(request, uid):
# uid = user id taken from profile url
me = User.objects.get(pk=uid)
if me != request.user:
raise Http404