Change URL Path for View Profile Page - Django - django

How would one go about creating a user-profile page that other users can view without being able to edit the profile unless they are the user?
The thing I'm trying to work out is how the url routing would work, is it best practice to store a user's profile on a profile/ or <user_id> page and then load in the individual user's data like recent posts using the username or id passed through the url?
Also would this be handled by the one view and template and just use {% if request.user == profile.user %} to display things like edit profile etc?
my problem is any user can edit for others there profiles when he edit url
for example my id is www.test.com/profile/44/ and other user have this id www.test.com/profile/40/
okay ,, now when i edit the link to be 40 not 44 i can access and edit the second user ! how to fix that
models.py :
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
email_confirmed = models.BooleanField(default=False)
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
def __str__(self):
return self.user
urls.py :
from django.urls import path
from blog_app.views import ProfileView
urlpatterns = [
path('profile/<int:pk>/', ProfileView.as_view(), name='profile'),
]
forms.py :
# Profile Form
class ProfileForm(forms.ModelForm):
# constructor of the UserForm, not Meta
def __init__(self, *args, **kwargs):
super().__init__(*args,**kwargs)
self.fields['username'].widget.attrs.update({'class':'form-control','placeholder':' Enter your username in English ','style': 'font-size:19px;text-align: center;'})
class Meta:
model = User
fields = [
'username',
'first_name',
'last_name',
'email',
]
views.py:
# Edit Profile View
class ProfileView(UpdateView):
model = User
form_class = ProfileForm
success_url = reverse_lazy('home')
template_name = 'user/commons/profile.html'
def get(self, request, *args, **kwargs):
form = self.form_class()
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.is_active = False # Deactivate account till it is confirmed
user.save()
current_site = get_current_site(request)
subject = 'Activate Your MySite Account'
message = render_to_string('user/emails/account_activation_email.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'token': account_activation_token.make_token(user),
})
user.email_user(subject, message)
messages.success(request, ('Please Confirm your new email to change email.'))
return redirect('login')
return render(request, self.template_name, {'form': form})
html page :
<button type="button" id="submit"> <a href="{% url 'profile' user.id %}" > edit profile info </a></button>

You can override the get_object() method to always return the currently logged on user from request.user, then you will not need to provide "pk" variable in your path.
Implement get_object() in your view
class ProfileView(UpdateView):
model = User
form_class = ProfileForm
success_url = reverse_lazy('home')
template_name = 'user/commons/profile.html'
def get_object(self, queryset=None):
return self.request.user
Then configure the path without pk
urlpatterns = [
path('profile/me/', ProfileView.as_view(), name='profile'),
]
Note that you should use login_required() decorator or LoginRequiredMixin on that view to avoid anonymous users accessing this view.

Related

Generic detail view UserRegister must be called with either an object pk or a slug in the URLconf

I'm getting this error when trying to register to my to-do list web
Generic detail view UserRegister must be called with either an object pk or a slug in the URLconf.
views.py
class UserRegister(CreateView):
model = User
form_class = UserRegisterForm
template_name = 'users/form_register.html'
redirect_authenticated_user = True
success_url = reverse_lazy('tasks')
# Forbid logged in user to enter register page
def get(self, *args, **kwargs):
if self.request.user.is_authenticated:
return redirect('tasks')
return super(UserRegister, self).get(*args, **kwargs)
# Send email verification
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = UserRegisterForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.is_active = False
user.save()
current_site = get_current_site(request)
subject = 'Activate your account'
message = render_to_string('users/account_activation_email.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'token': account_activation_token.make_token(user),
})
user.email_user(subject, message)
return redirect('login')
else:
return self.form_invalid(form)
urls.py
urlpatterns = [
path('register/', UserRegister.as_view(), name='register'),
path('login/', UserLogin.as_view(), name='login'),
path('logout/', LogoutView.as_view(next_page='login'), name='logout'),
path('activate/<uidb64>/<token>/', ActivateAccount.as_view(), name='activate'),
]
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
Maybe it got something to do with the models.py? But to be honest I don't really know what else to put there.
Keep in mind I have two apps, the user app for registration and login and whatnot, and the tasks app for displaying tasks, creating, deleting task, etc.

Protection against editing another user's profile in django

I have trouble with protection against editing another user's profile and I don't have any idea how to solve this issue.
Ofcourse I know that I can do sth like this:
<a class="nav-item nav-link" href="{% url 'profile_change' user.pk %}">
or something similar.
But I don't know what i can do in situation when user write him/herself webadress. I mean situation when user with pk = 2, would write adress: "website address/profile/change/1"
Here is my models, views and urls:
#urls.py
urlpatterns = [
path('profile/', views.profile, name='profile'),
path('profile/list/', views.ProfileListView.as_view(), name='profile_changelist'),
path('profile/add/', views.ProfileCreateView.as_view(), name='profile_add'),
path('profile/change/<int:pk>/', views.ProfileUpdateView.as_view(), name='profile_change')
]
#views.py
class ProfileCreateView(CreateView):
model = Profile
form_class = ProfileForm
success_url = reverse_lazy('profile_changelist')
def form_valid(self, form, **kwargs):
form.instance.user = self.request.user
return super().form_valid(form)
class ProfileUpdateView(UpdateView):
model = Profile
fields = ('first_name', 'last_name', 'year')
success_url = reverse_lazy('profile_changelist')
#login_required
def profile(request):
if Profile.objects.filter(user=request.user.id).count() == 1:
return render(request, 'profiles/profile.html')
else:
return HttpResponseRedirect('add')
#models.py
class Profile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
first_name = models.CharField(max_length=100, null=True)
last_name = models.CharField(max_length=100, null=True)
year = models.IntegerField(choices=YEARS, default=1)
You must check what the user from request object is a editable user from parameters:
from django.http import Http404
class ProfileUpdateView(UpdateView):
model = Profile
fields = ('first_name', 'last_name', 'year')
success_url = reverse_lazy('profile_changelist')
def get(self, request, *args, **kwargs):
if not request.user.id == self.kwargs.get('pk'):
raise Http404
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
if not request.user.id == self.kwargs.get('pk'):
raise Http404
return super().post(request, *args, **kwargs)

How to restructure my Django code?

I am new to Django.I want to create app that would enable selected users to login and then upload files that would latter be processed.
models.py
class Profile(models.Model):
username = models.OneToOneField(User, on_delete=models.CASCADE)
password = models.TextField(max_length=80,blank=True)
company = models.TextField(max_length=80, blank=True)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
class Document(models.Model):
uploaded_by = models.ForeignKey(Profile,on_delete=models.CASCADE)
date_uploaded = models.DateTimeField(auto_now_add=True)
forms.py
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
company = forms.CharField()
class DocumentForm(forms.Form):
docfile = forms.FileField(label='Select a file')
malex.urls(application urls)
from malex.views import list
from malex.views import login
urlpatterns = [
url(r'^list/$', list, name='list'),
url(r'^login/$', login, name='login'),
]
project/urls
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^newp/', include('malex.urls')),
]
views.py
def login(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
user = authenticate(user=cd['user'],password=cd['password'],company=cd['company'])
if user is not None:
if user is active:
login(request,user)
return HttpResponse('Authenticated successfully')
else:
return HttpResponse('Disabled account')
else:
return HttpResponse('Invalid login')
else:
form=LoginForm()
return render(request,'account/login.html',{'form': form})
def list(request):
# Handle file upload
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile=request.FILES['docfile'])
newdoc.save()
# Redirect to the document list after POST
return HttpResponseRedirect(reverse('list'))
Now the login and upload operations are separated.
How to change my views and urls to have login first and upload latter?
Do I need to use Class based views with decorators?
create a custom login form and view.
use your localhost:8000 address as login template
(app) urlpatterns = [path('',views.loginview,name = 'login')]
(project) urlpatterns = [path('', include('malex.urls')),)]
extend your login template using {% block content %}{% endblock %}
use pass restrictions {% if request.user.is_authenticated %} in template or view to let them access upload section.

django too many redirects after login

I am running: Django==2.0.6 / python==3.6.5 / django-allauth==0.36.0
I have not touched this project in several weeks. I upgraded a number of packages before starting this round of development.
I can currently create a new user account and verify the email address used as the username. The user model is "extended" in that I use the email address for the username and have a "Profile" class to hold additional interesting fields.
I was once able to create a new account, verify the email address and be taken to a form to fill out that additional interesting info. Now, after email verification there is a noticable pause and then I am told that I have ben redirected too many times.
My urls.py looks like:
from django.urls import path, re_path
from .views import index
from .views import profile
urlpatterns = [
re_path(r'^', index, name='index'),
path('Profile/<uuid:account_number>/edit', profile.Update.as_view(), name="profile-update"),
path('Profile/<uuid:account_number>/delete', profile.Delete.as_view(), name="profile-delete"),
path('Profile/create', profile.Create.as_view(), name="profile-create"),
]
The Profile CBV is:
import django.contrib.messages as messages
from allauth.account.decorators import verified_email_required
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.shortcuts import render
from django.utils.decorators import method_decorator
from django.views.generic import CreateView, UpdateView, DeleteView
from Members.forms.profile import ProfileForm
from Members.models.profile import Profile
import datetime
#method_decorator(verified_email_required, name='dispatch')
class Create(CreateView):
model = Profile
template_name = 'Members/profile.html'
form_class = ProfileForm
success_url = 'Members/index.html'
def get(self, request, *args, **kwargs):
return render(request, self.template_name, {
'profileForm': self.form_class(),
'profileState': "create"
})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
profile = form.save(commit=False)
profile.user = self.request.user
profile.save()
my_render = render(request, self.success_url, {
'profile': profile,
'profileState': "display"
})
else:
#
# form has issues. send it back to the create form to fix.
my_render = render(request, self.template_name, {
'profileForm': form,
'profileState': "editForCreate"
})
return my_render
#method_decorator(verified_email_required, name='dispatch')
class Update(UpdateView):
pk_url_kwarg = "account_number"
model = Profile
template_name = 'Members/profile.html'
form_class = ProfileForm
success_url = 'Members/index.html'
def get(self, request, *args, **kwargs):
profile = Profile.objects.get(account_number=self.kwargs[self.pk_url_kwarg])
form = ProfileForm(instance=profile)
return render(request, self.template_name, {
'profileForm': form,
'profileState': "edit"
})
def post(self, request, *args, **kwargs):
profile = Profile.objects.get(account_number=self.kwargs[self.pk_url_kwarg])
form = ProfileForm(request.POST, instance=profile)
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
profile.dateModified = datetime.datetime.now()
profile.save()
my_render = render(request, 'Members/index.html', {
'profile': profile
})
else:
#
# form has issues. send it back to the create form to fix.
my_render = render(request, self.template_name, {
'profileForm': form,
'profileState': "editForUpdate"
})
return my_render
#method_decorator(verified_email_required, name='dispatch')
class Delete(DeleteView):
pk_url_kwarg = "account_number"
model = Profile
# form_class = ProfileForm
success_url = "account/login.html"
def get(self, request, *args, **kwargs):
try:
#
# I can't believe django does not honor on_delete cascade.
# Have to roll your own. Tsk
owner = self.request.user
profile = get_object_or_404(Profile, account_number=self.kwargs[self.pk_url_kwarg])
user_pk = profile.user.pk
profile.delete()
get_object_or_404(User, pk=user_pk).delete()
messages.success(request, "The user is deleted")
except User.DoesNotExist:
messages.error(request, "User does not exist")
# except IntegrityError:
# messages.error(request, "DB IntegrityError")
return redirect("accounts/login/")
How do you go about debugging this? Ive tried setting breakpoints in PyCharm but they are ignored or ar in the wrong places.
in your urls.py you need to add $ in order to close your regular expression
urlpatterns = [
re_path(r'^$', index, name='index'),
]

Add username to URL with AuthenticationForm FormView

I'm coming from Laravel and new to Django. I'm trying to add a username to the url after login. This has been asked before a few times, but I have yet to make the solutions work (they involve having a model attached to the generic FormView class). Here is what I have:
urls.py
path('login/', views.Login.as_view(), name='login'),
# Logged in user
path('home/<str:username>', views.UserIndex.as_view(), name='user_index'),
views.py
class Login(views.AnonymousRequiredMixin, views.FormValidMessageMixin, generic.FormView):
authenticated_redirect_url = '/'
form_class = LoginForm
form_valid_message = "You have successfully logged in"
template_name = 'pythonmodels/registration/login.html'
success_url = reverse_lazy('pythonmodels:user_index', args=("Bill",))
def form_valid(self, form):
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = authenticate(username=username, password=password)
if user is not None and user.is_active:
login(self.request, user)
return super(Login, self).form_valid(form)
else:
return self.form_invalid(form)
forms.py
class LoginForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
'username',
'password',
ButtonHolder(
Submit('login', 'Login', css_class='btn-primary')
)
)
In the views.py file, I would like the args for success_url to be the username of the user that was just authenticated. Should this be done in the LoginForm class? I have also seen that you can go to an intermediate url and then get the User data, but this seems like a terrible extra step. I would like to keep this as close to the base FormView and AuthenticationForm as I don't understand more in-depth customization yet. Thanks so much!
You can't set success_url in the view, because you don't know the argument until after the user has been logged in.
Override get_success_url instead:
def get_success_url(self):
return reverse('pythonmodels:user_index', args=[self.request.user.username])