Viewing other user profile - python

I want users click on other users to visit their profile without editing their posts.
Here is my Profil view:
def Profil(request, username):
if not request.user.is_authenticated():
return render(request, 'blog/visitor.html')
else:
u = User.objects.get(username=username)
user = request.user
posts = Post.objects.filter(user=request.user)
context = {'user': user, 'user_url':u,'posts': posts}
return render(request, 'blog/profil.html', context)
And here my Index view
def IndexView(request):
if request.user.is_authenticated():
base_template_name = 'blog/base.html'
else:
base_template_name = 'blog/visitor.html'
posts = Post.objects.all()
return render(request, 'blog/index.html',
{'posts':posts, 'base_template_name':base_template_name})
( Is there any other solution apart this ? )
My urls.py
url(r'^(?P<username>\w+)/$', views.Profil, name = 'profil'),
and a link from index.html to profil.html looks like this:
{{post.user.username }}
the problem with this is when i click on the link it passes with the current user I'm logging in with and not the user that I click on it
LogIn with admin and want to see Ala's profile:
When I click it shows in the url this:
http://127.0.0.1:8000/blog/imedadmin/ which is supposed to be 'Ala' instead of 'imedadmin'.

In my opinion, you can send a variable to view in your render function, call it isHim. When logged in user is himself, set it equal to true and when it's not, set it to false. In you're view, check it with {{if isHim}} and then enable/disable edit buttons. I've used it and it answered.

You could do something like :
{% if request.user.id == post.user.id %}
# ur code {% endif %}
in the templates dir.

Related

Django form errors appearing without error

I made a research here in Stack and my problem is the opposite of the majority, I saw some ways to make it appear, but my problem is that it's appearing when the user hits the "Register" button / Refresh the register page. So it's an annoying thing that appears wherever the user enter/refresh the page because the form is empty.
View.py
#unauthenticated_user
def register(request):
form_u = CreateUser(request.POST)
form_c = CreateClient(request.POST)
if request.method == 'POST':
form_u = CreateUser(request.POST)
form_c = CreateClient(request.POST)
if form_u.is_valid() and form_c.is_valid():
user = form_u.save()
group = Group.objects.get(name='func')
user.groups.add(group)
client = form_c.save(commit=False)
client.user = user
client.save()
return redirect('login')
else:
form_u = CreateUser()
form_c = CreateClient()
context = {'form_u': form_u, 'form_c': form_c}
return render(request, 'register.html', context)
HTML
<form method="POST" action="" id="ativa">
{% csrf_token %}
...
</form>
{{form_u.errors}}
{{form_c.errors}}
<div class="mt-4">
<div class="d-flex justify-content-center links">
Have an account ? Login
</div>
</div>
Print
P.S: The site is in portuguese, but I can share the form link in heroku
Your logic is opposite of what you want:
Initialize the forms with POST data regardless of whether the request is a POST or a GET request, which will result in the errors if there is no POST data.
Then you initialize empty forms when the form data is invalid.
Instead you'll want to pass POST data only if the request is a POST request, and you should initialize empty forms only if the request is not a POST request:
#unauthenticated_user
def register(request):
# If request is POST, validate forms and add objects.
if request.method == 'POST':
form_u = CreateUser(request.POST)
form_c = CreateClient(request.POST)
if form_u.is_valid() and form_c.is_valid():
user = form_u.save()
group = Group.objects.get(name='func')
user.groups.add(group)
client = form_c.save(commit=False)
client.user = user
client.save()
return redirect('login')
# We can remove the else statement here,
# because the function either redirects or resumes
# normal flow and renders the template
# with the form errors.
else:
# Only initialize empty forms when no POST request was made.
form_u = CreateUser()
form_c = CreateClient()
context = {'form_u': form_u, 'form_c': form_c}
return render(request, 'register.html', context)

Django Views, having 2 redirects in 1 view

Is it possible to have 2 redirect() in the same django view. so when the like button is in the home page, i want it to redirect back to home page, if like button is in detail page, i want to redirect back to detail page?
For instance:
def LikeView(request, slug):
context = {}
post = get_object_or_404(BlogPost, slug=slug)
post.likes.add(request.user)
if in homepage:
return redirect('HomeFeed:detail', slug=slug)
else:
return redirect('HomeFeed:main')
def delete_blog_view(request,slug):
context = {}
user = request.user
#YOU WANT to check if user is authenticated. if not, you need to authenticate! redirect you to that page
if not user.is_authenticated:
return redirect("must_authenticate")
account = Account.objects.get(pk=user_id)
blog_post = get_object_or_404(BlogPost, slug=slug)
blog_post.delete()
return redirect(request.META.get('HTTP_REFERER', 'account:view', kwargs={'user_id': account.pk }))
Pass the redirect URL in a next URL param. Like so:
<!-- In homepage template -->
Like
<!-- in Detail template -->
Like
or simply:
Like
To always pass the current URL as the redirect URL.
And then in your LikeView:
def LikeView(request, slug):
...
next = request.GET.get("next", None)
if next and next != '':
return HttpResponseRedirect(redirect_to=next)
# Then have a default redirect URL in case `next` wasn't passed in URL (Home for Example):
return HttpResponseRedirect(redirect_to="/home")
As mentioned in the Django Docs, this isn't safe (for most apps), so you have to check if URL is safe then redirect to next otherwise just return a default safe in-app URL.
Read on the url_has_allowed_host_and_scheme function to check if URL is safe on this Docs page

Using Django Context Processors with forms

I have multiple forms to be shown everywhere in my project and hence I read that having a context_processor was the best way to do it. So, I created one inside my app and it looks something like this:
def forms_processor(request):
name_form = NewNameForm()
work_form = NewWorkForm()
address_form = NewAddressForm()
context = {'name_form': name_form,
'work_form': work_form,
'address_form': work_form,
}
return context
This works great, I can just use {{name_form}} anywhere in my templates and that renders the form.
Now my question is, where do I validate the form? In my views.py or the context_processors.py? Right now my views for name_form looks something like this:
def user_profile(request):
if request.method == 'POST':
name_form = NewNameForm(request.POST)
if name_form.is_valid():
form.save()
else:
ctx = {'title': 'Profile', 'active_tab': 'Profile'}
return render (request, 'user_profile.html', ctx)
This isn't working actually, if I submit an invalid form, it just comes back to the same page and won't show a populated form.
If someone could guide me or redirect me to some docs on this topic, that'd be awesome! Thanks!
The problem is that your processor instantiates the form on each render. Each time you call render, your processor is called, which instantiates a new form and displays THAT form, not the form instance that you created in the view. Therefore, the form being rendered is a blank instance but the form that contains the input and errors was destroyed by garbage collection after finishing your view.
A way I would do this, is passing the form you create in the view back to context before rendering. Pass it in to a context key such as "name_form_filled". Then if that variable is present in the context, don't render "name_form", instead render "name_form_filled".
views.py
def user_profile(request):
ctx = {}
if request.method == 'POST':
name_form = NewNameForm(request.POST)
if name_form.is_valid():
name_form.save() # you named this named_form, not form.
# If you want to redirect to another view when the form is saved successfuly, do it here.
else:
ctx["name_form_filled"] = form
else:
ctx.update({'title': 'Profile', 'active_tab': 'Profile'})
return render (request, 'user_profile.html', ctx)
user_profile.html
<div id="form_container">
{% if name_form_filled %}
<!-- Render form that has input and errors from previous POST. -->
{{ name_form_filled }}
{% else %}
<!-- render empty initial form. User has not attempted to submit yet. -->
{{ name_form }}
{% endif %}
</div>
===========================================================================
Another way you could do this is turn this view into a class based view and inherit a base class based view. This base class will override the get_context_data method and add your three forms. Note that you won't be using the context processor with this methodology so you could get rid of it if wanted in this case.
All views that use your form will extend the base view class. Then, after evaluating your form, if it is invalid, overwrite your name_form context key with the invalid form instance, which will be in your context.
views.py
class BaseView(View):
def get_context_data(self, *args, **kwargs):
context = {
"name_form": NewNameForm(),
"work_form": NewWorkForm(),
"address_form": NewAddressForm()
}
return context
class UserProfileView(BaseView):
def get(self, request, *args, **kwargs):
# Do GET logic here.
ctx = self.get_context_data(*args, **kwargs) # BaseView.get_context_data will be called here unless you override it in this class.
ctx.update({'title': 'Profile', 'active_tab': 'Profile'})
return render (request, 'user_profile.html', ctx)
def post(self, request, *args, **kwargs):
# Do POST logic here.
ctx = self.get_context_data(*args, **kwargs) # BaseView.get_context_data will be called here unless you override it in this class.
name_form = NewNameForm(request.POST)
if name_form.is_valid():
name_form.save()
else:
ctx["name_form"] = name_form # will replace the empty form in context with the form instance created in name_form that has input and errors.
return render (request, 'user_profile.html', ctx)
user_profile.html
<div id="form_container">
<!-- Will render whatever is in name_form. If this is after the
user has submitted an invalid form, this form will be populated with input and errors because we overwrote it in the view. -->
{{ name_form }}
</div>
===========================================================================
I personally think that the first solution is the best but when you start getting more complex, you should probably switch over to the second solution as class based views make complex views way easier.
Direct answer: you validate the form in views.py with is_valid() method. What you need is to populate context with bound form if the form is invalid:
def user_profile(request):
ctx = {'title': 'Profile', 'active_tab': 'Profile'}
if request.method == 'POST':
name_form = NewNameForm(request.POST)
if name_form.is_valid():
form.save()
return redirect(YOUR_REDIRECT_URL) # Always redirect after successful POST
ctx['form'] = form # if form is invalid return it with context
return render (request, 'user_profile.html', ctx)
Read more in documentation.

Django redirect, but pass form error context

I have a very simple index page view, from which the user can fill in a login popup, which sends a post request to /login
def index(request):
"""Shows list of studyspaces, along with corresponding 'busyness' score"""
context = {'study_space_list': StudySpace.objects.order_by('-avg_rating')}
if request.user.is_authenticated():
context['user'] = request.user
else:
context['login_form'] = LoginForm()
context['user_form'] = UserForm()
context['student_form'] = StudentForm()
return render(request, 'spacefinder/index.html', context)
If the login is valid it simply redirects to the index page, this works fine.
The login view looks as follows:
def user_login(request):
form = LoginForm(request.POST)
if request.method == 'POST' and form.is_valid():
user = form.login(request)
if user:
login(request, user)
return redirect(reverse('spacefinder:index'))
# Load the context all over again
context = {
'study_space_list': StudySpace.objects.order_by('-avg_rating')
}
context['login_form'] = form
context['user_form'] = UserForm()
context['student_form'] = StudentForm()
return render(request, 'spacefinder/index.html', context)
However when the login is incorrect I want to be able to refresh the page and show the login form errors inside the index template (in the login popup)
I'm actually able to achieve this with the above code, but I'm unhappy with the solution for the following reasons:
I have to manually fetch the context all over again, e.g user/student forms and studyspaces, this goes against the DRY principle
When the page is refreshed the url is localhost:8000/spacefinder/login
Screenshot of behaviour here
I'm wondering if there's somehow a way to use redirect to reload the index page and somehow pass errors from my login_form, e.g. something like:
return redirect('spacefinder:index', {'login_form': form})
I've looked into using messages to pass form validation errors, but struggled to get this working since Validation Errors are thrown inside forms.py, and I'm unable to fetch the request instance from inside a ModalForm to properly create a message
You are doing it the wrong way around.
Consider these prerequisites:
entry point to your page is the index view
the index view must only be accessible by authenticated users
the login view allows both methods GET and POST and is accessible to anonymous users only
The reason to use Django is to make use of all the features that it offers, and that includes handling of the above (because that is what most pages need, not only you).
To set it up correctly you need to define your urls.py like this:
from django.contrib.auth.decorators import login_required
urlpatterns = [
....
url('^login/$', user_login, 'login'),
url('^/$', login_required(index), 'index'),
....
]
In your settings/base.py (or settings.py if you have no environment differentiation) tell Django how to redirect users:
LOGIN_URL = reverse_lazy('login')
LOGIN_REDIRECT_URL = reverse_lazy('index')
https://docs.djangoproject.com/en/1.9/ref/settings/#login-url
https://docs.djangoproject.com/en/1.9/ref/settings/#login-redirect-url
Simplify your index view:
def index(request):
"""Shows list of studyspaces, along with corresponding 'busyness' score"""
context = {'study_space_list': StudySpace.objects.order_by('-avg_rating')}
if request.user.is_authenticated():
context['user'] = request.user
else:
return HttpResponseForbidden() # prevented by Django, should never happen
return render(request, 'spacefinder/index.html', context)
Let the user_login view deliver the empty login form:
#require_http_methods(["GET", "POST"])
def user_login(request):
params = getattr(request, request.method)
form = LoginForm(params)
if request.method == 'POST' and form.is_valid():
user = form.login(request)
if user:
login(request, user)
return redirect(reverse('spacefinder:index'))
# Load the context for new form or form with errors
context = {
'study_space_list': StudySpace.objects.order_by('-avg_rating')
}
context['login_form'] = form
context['user_form'] = UserForm()
context['student_form'] = StudentForm()
return render(request, 'spacefinder/index.html', context)
You have not presented any code that handles the UserForm or the StudendForm. You would need to add that to the user_login view, as well - if this is something that all users should fill in every time they login. Otherwise use a different view.
It's worth looking at modules like allauth. They might spare you some work when it comes to allowing users to login with their e-mail addresses, ascertain that e-mail addresses are unique in the system etc.

Django Url not redirecting correctly on user creation

I have a user sign up and login template set to send information to the same view (detail). They were both working fine before, however now the redirect on user creation is no longer going to the correct URL (http://127.0.0.1:8000/accounts/login/?next=/accounts/21/) the "accounts/login?next=" portion of the URL is being added for some reason and I cannot understand where it came from as it was not there before.
I'm using stronghold which makes every view login_required unless noted otherwise with #public above it.
I have found some posts about LOGIN_URL needs to be set in setting.py or a next key. However this was working fine before so I do not think that is the problem. let me know if you need more code posted and I will put it up.
Thanks,
-the route I want to hit is
url(r'^accounts/(?P<user_id>\d+)/$', views.detail, name='detail')
-my register view is below
#public
def register(request):
if request.method == 'POST':
form = EmailUserCreationForm(request.POST)
if form.is_valid():
new_user = form.save()
playthrough = PlayThrough(user_id=new_user.id)
playthrough.save()
request.session['user_id'] = new_user.id
return HttpResponseRedirect('/accounts/{}/'.format(new_user.id))
else:
form = EmailUserCreationForm()
return render(request, 'dep_server/register.html', {
'form': form,
})
-this is he view that is supposed to render the user info
def detail(request, user_id):
if request.session['user_id'] == int(user_id):
user = EmailUser.objects.get(id=user_id)
module_list = ModuleRef.objects.all()
return render(request, 'dep_server/detail.html', {
'user': user,
'module_list': module_list
})
else:
return HttpResponseRedirect('/accounts/auth/')
I figured out the problem, I was not logging in the user on creation. Which is why the login worked and the sign up did not. below is the code that I added to the register view, which got it to work.
user = authenticate(
email = form.cleaned_data['email'],
password = form.cleaned_data['password2']
)
login(request, user)

Categories

Resources