Following code is working for comment section.
from django.db import models
from django.contrib.sites.models import Site
from django.db.models import signals
from notification import models as notification
def new_comment(sender, instance, created, **kwargs):
# remove this if-block if you want notifications for comment edit too
if not created:
return None
context = {
'comment': instance,
'site': Site.objects.get_current(),
}
recipients = []
# add all users who commented the same object to recipients
for comment in instance.__class__.objects.for_model(instance.content_object):
if comment.user not in recipients and comment.user != instance.user:
recipients.append(comment.user)
# if the commented object is a user then notify him as well
if isinstance(instance.content_object, models.get_model('auth', 'User')):
# if he his the one who posts the comment then don't add him to recipients
if instance.content_object != instance.user and instance.content_object not in recipients:
recipients.append(instance.content_object)
notification.send(recipients, 'friends_invite', context)
signals.post_save.connect(new_comment, sender=models.get_model('comments', 'Comment'))
But when we implement the same code for other custome model comment then it giving error at the time of entering the record in model.
from django.db import models
from django.contrib.sites.models import Site
from django.db.models import signals
from notification import models as notification
def create_notice_types(app, created_models, verbosity, **kwargs):
notification.create_notice_type("friends_invite", _("Invitation Received"), _("you have received an invitation"))
notification.create_notice_type("friends_accept", _("Acceptance Received"), _("an invitation you sent has been accepted"))
signals.post_syncdb.connect(create_notice_types, sender=notification)
def new_comment(sender, instance, created, **kwargs):
# remove this if-block if you want notifications for comment edit too
if not created:
return None
context = {
'gufeed_feedcomment': instance,
}
recipients = []
# add all users who commented the same object to recipients
for comment in instance.__class__.objects.for_model(instance.content_object):
if comment.user not in recipients and comment.user != instance.user:
recipients.append(comment.user)
# if the commented object is a user then notify him as well
if isinstance(instance.content_object, models.get_model('auth', 'User')):
# if he his the one who posts the comment then don't add him to recipients
if instance.content_object != instance.user and instance.content_object not in recipients:
recipients.append(instance.content_object)
notification.send(recipients, 'friends_invite', context)
signals.post_save.connect(new_comment, sender=models.get_model('gufeed_feedcomment', 'FeedHottPoint'))
Note: we are taking friends_invite notification type this is already created
ERROR:
AttributeError at /gufeed/comment/add/
'Manager' object has no attribute 'for_model'
Related
I checked the other posts on here that have the attribute error that I have, but they seem to be for different reasons. I am currently requesting the information from a form for users to update a project page. Then, if the form is valid, I am saving the form, saving the project, then trying to return redirect to the project page; however, when I click the button, the computer renders the error page. I will attach my forms.py, views.py, models.py, and urls.py:
Views.py for the update section:
#wraps(function)
def wrap(request, *args, **kwargs):
user = request.user
name = kwargs.get('name')
if uProjects.objects.filter(project=Project.objects.get(name=name), user=user, ifAdmin=True).exists():
return function(request, *args, **kwargs)
else:
return HttpResponseRedirect('/')
return wrap
#admin_check
def update(request, name):
project = Project.objects.get(name = name)
if request.method == "POST":
pr_form = ProjectUpdateForm(request.POST,
request.FILES,
instance=project)
#if is_admin in Member == True: #need to authenticate user, access user permissions, if user has permission:
if pr_form.is_valid():
pr_form.save()
messages.success(request, f'This project has been updated.')
request.project.save()
return redirect('project')
else:
pr_form = ProjectUpdateForm(instance=project)
context = {
'pr_form': pr_form
}
return render(request, 'projects/updateproject.html', context)
forms.py for ProjectUpdateForm:
class ProjectUpdateForm(forms.ModelForm):
class Meta:
model = Project
fields=['name', 'department', 'department','bPic', 'logo',
'department', 'purpose', 'projectTag', 'lookingFor', 'recruiting']
urls.py
from projects import views as p
path('project/<str:name>/', p.project, name='project'),
path('editproject/<str:name>/', p.update, name="editproject"),
Thanks, please let me know what I can do.
Your error is in line request.project.save(), request doesn't have project attribute.
And actually you don't need to call save() method for project.
Because ProjectUpdateForm is the ModelForm and ModelForm.save() (Django docs) method will create a new instance of the specified model or update assigned instance.
#admin_check
def update(request, name):
project = Project.objects.get(name = name)
if request.method == "POST":
pr_form = ProjectUpdateForm(request.POST,
request.FILES,
instance=project)
#if is_admin in Member == True: #need to authenticate user, access user permissions, if user has permission:
if pr_form.is_valid():
# save() returns an instance object, you can use it to manipulate your object.
instance = pr_form.save()
messages.success(request, f'This project has been updated.')
# YOUR ERROR IS ⬇️ HERE request doesn't have project attribute
# request.project.save()
# redirect with arguments
return redirect('project', name=instance.name)
...
Also your redirect must contain argument name, because your project url required name attribute:
redirect('project', name=instance.name)
I'm pretty confused about how do I prevent users' from accessing the data of other users.
The case at hand :
I'm creating a Notes + To-Do app in which a user logs in, creates their notes and tasks.
How to create links to those notes such that they aren't accessible by other users? As in the correct syntax for UserPassesTestMixin.
In the To-Do app, how do I keep the tasks of one user unique to them? Similarly for the note app, how do I achieve that?
Not sure what you mean by "create links". For what you describe, the links don't change for people that have access or not. The difference if that a user that owns note 5 and goes to /note/5/, they should be able to see their note, but if another user goes to /note/5/ they should either 1) get a 404 error (Note not found) or 403 (Permission Denied) just be redirected to another page (say, the home page), maybe with a message.
Using Class based views, this is easy to do.
Prevent access to views
from django.core.exceptions import PermissionDenied
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
class LoginRequiredAccessMixin(object):
# This will ensure the user is authenticated and should
# likely be used for other views
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(LoginRequiredAccessMixin, self).dispatch(request, *args, **kwargs)
class AccessMixin(LoginRequiredAccessMixin):
def get_object(self, queryset=None):
obj = get_object_or_404(Note, pk=self.kwargs['id'])
# Assumes you have a notes.user, but change to created_by
# or whatever is your user field name
if obj.user == self.request.user:
# User owns object
return obj
raise PermissionDenied("User has no access to this note")
class NoteView(AccessMixin, DetailView):
# This is a regular DetilView, but with the Mixin,
# you are overwriting the get_object() function.
# If you don't want the Mixin, then you can just add
# get get_object() function here. Except that with the
# Mixin, you can reuse it for your UpdateView, DeleteView
# and even across both your notes and task views
model = Note
template_name = 'note/details.html'
def get_context_data(self, **kwargs):
context = super(NoteView, self).get_context_data(**kwargs)
# Add any special context for the template
return context
If instead you want to just direct users to another page, you would do something like:
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from django.contrib import messages
class NoteView(DetailView):
model = Note
template_name = 'note/details.html'
def get_context_data(self, **kwargs):
context = super(NoteView, self).get_context_data(**kwargs)
# Add any special context for the template
return context
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
note = self.get_objet()
if note and not note.user == self.request.user:
messages.error(
self.request,
'You are not allowed to access this Note'
)
return HttpResponseRedirect('/home')
return super(NoteView, self).dispatch(request, *args, **kwargs)
You didn't supply any code so I cannot be more specific, but hopefully you get an idea of the two techniques. The first is usually a cleaner solution, and the Mixin I show can be shared across both your Note views and ToDo Tasks records, assuming they use the same user/created_by field name.
In case you are using functions (FBV) you could use if request.user == item.user
#login_required
def post_edit(request, post_id):
item = Post.objects.get(pk=post_id)
if request.user == item.user:
CBV - Class Based View - using UserPassesTestMixin
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
# [...]
You can use the decorator in Django called "user_passes_test"
You can import like:
from django.contrib.auth.decorators import user_passes_test
For detail check docs here
In my application I am using Django Allauth. I don't have any registration form for users. The admin is going to register users by uploading an excel file that contains user info. I have done all of this and users are saved in the user table by auto generating passwords. After I upload user lists and save them in database, I want to send a reset password email to each user.
In allauth to reset password you first need to go to reset page account/password/reset/ and type your email. then an email is send which directs you to change your password account/password/reset/key/(?P<uidb36>[0-9A-Za-z]+)-(?P<key>.+)/
Is it possible to send the email directly within the app? The url contains a key that I don't know how to generate!! Or is there any better way to do that?
It's possible. My solution implements a User model post_save signal to call the Allauth Password reset view which will send the user the email. The first thing to consider is to make the user email address mandatory in the admin user create form (as explained here). And then use this code:
from allauth.account.views import PasswordResetView
from django.conf import settings
from django.dispatch import receiver
from django.http import HttpRequest
from django.middleware.csrf import get_token
#receiver(models.signals.post_save, sender=settings.AUTH_USER_MODEL)
def send_reset_password_email(sender, instance, created, **kwargs):
if created:
# First create a post request to pass to the view
request = HttpRequest()
request.method = 'POST'
# add the absolute url to be be included in email
if settings.DEBUG:
request.META['HTTP_HOST'] = '127.0.0.1:8000'
else:
request.META['HTTP_HOST'] = 'www.mysite.com'
# pass the post form data
request.POST = {
'email': instance.email,
'csrfmiddlewaretoken': get_token(HttpRequest())
}
PasswordResetView.as_view()(request) # email will be sent!
You can try to get URL for specific user using something like this:
from allauth.account.forms import EmailAwarePasswordResetTokenGenerator
from allauth.account.utils import user_pk_to_url_str
token_generator = EmailAwarePasswordResetTokenGenerator()
user = User.objects.get(email='example#example.com')
temp_key = token_generator.make_token(user)
path = reverse("account_reset_password_from_key",
kwargs=dict(uidb36=user_pk_to_url_str(user), key=temp_key))
Following from #davecaputo's answer, you can also directly submit the form instead of creating and calling the view:
from allauth.account.forms import ResetPasswordForm
from django.conf import settings
from django.http import HttpRequest
def send_password_reset(user: settings.AUTH_USER_MODEL):
request = HttpRequest()
request.user = user
request.META["HTTP_HOST"] = "www.mysite.com"
form = ResetPasswordForm({"email": user.email})
if form.is_valid():
form.save(request)
Actually i'am very new to django and python. In /templates/home.html, added {{ user.username }} it's showing currently logged in username
<p>Welcome {{ user.username }} !!!</p>
I want to get currently logged in username in views.py file. How to get username?
i tried different way but i am not get the result
user = User.objects.get(username=request.user.username)
username = request.GET['username']
def sample_view(request):
current_user = request.user
print(current_user)
Please tell me, How to achieve my result.
my views.py look like this. is there any problem on my views.py
#!python
#log/views.py
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from django.template import Context
from contextlib import contextmanager
# Create your views here.
# this login required decorator is to not allow to any
# view without authenticating
#login_required(login_url="login/")
def home(request):
return render(request,"home.html")
#dummy_user = {{ username }}
#user = User.objects.get(username=request.user.username)
#username = request.GET['username']
#print(usernam
#user = request.user
#print(user)
def sample_view(request):
current_user = {}
#current_user['loggeduser'] = request.user
#or
current_user['loggeduser'] = request.user.username
return render(request,"home.html",current_user)
# print(current_user.id)
Provided that you have enabled the authentication middleware, you don't need to do any of this. The fact that the username shows up in your template indicates that you have enabled it. Each view has access to a request.user that is an instance of a User model. So the following is very much redundant
user = User.objects.get(username=request.user.username)
Because you already have request.user.username!! if you wanted to find the user's email, you do request.user.email Or just do
user = request.user
and use the newly created variable (eg user.username)
Reference: https://docs.djangoproject.com/en/1.10/ref/request-response/#django.http.HttpRequest.user
From the AuthenticationMiddleware: An instance of AUTH_USER_MODEL
representing the currently logged-in user. If the user isn’t currently
logged in, user will be set to an instance of AnonymousUser. You can
tell them apart with is_authenticated, like so:
I've encountered a problem where I had to load more than 200 new users into my django app and right away send them a password reset email. This had to happen only once, done only by me and run quietly on backend. Surfing the internet brought me only to one more or less right answer: Trigger password reset email in django without browser?. The only problem was is that this post was about 4 years old and of course when I tried the solution, it didn't work...
Two most valuable points from the link I mentioned:
In most recent version of Django, we need to call form.is_valid()
Sending of email is done upon save().
Here is how I queried users that I needed and sent each of them a password reset link:
def find_users_and_send_email():
from django.http import HttpRequest
from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.models import User
import logging
logger = logging.getLogger(__name__)
users = User.objects.filter(date_joined__gt = '2015-04-16')
for user in users:
try:
if user.email:
logger.info("Sending email for to this email:", user.email)
form = PasswordResetForm({'email': user.email})
assert form.is_valid()
request = HttpRequest()
request.META['SERVER_NAME'] = 'help.mydomain.com'
request.META['SERVER_PORT'] = '443'
form.save(
request= request,
use_https=True,
from_email="username#gmail.com",
email_template_name='registration/password_reset_email.html')
except Exception as e:
logger.warning(str(e))
continue
return 'done'
Usually PasswordResetForm works with a "request" from the front-end, which I didn't have. So I simply created one.
When I followed the example in the link, it failed.. It couldn't find server name in the request. (Makes sense because I instantiated my request out of nowhere)
When I fixed server name, it asked for the server port. Since I used https, my port is 443, otherwise, use default port.
If you use https, don't forget to indicate it when you save the form use_https=True
I found it useful to wrap #chabislav's answer up in a custom management command which can then be reused as necessary. Note that I eliminated the user object filter, as I knew that I wanted to send password reset instructions to all users.
# myproject/myapp/management/commands/send_password_reset_emails.py
from django.core.management.base import BaseCommand, CommandError
# I use a custom user model, so importing that rather than the standard django.contrib.auth.models
from users.models import User
from django.http import HttpRequest
from django.contrib.auth.forms import PasswordResetForm
class Command(BaseCommand):
help = 'Emails password reset instructions to all users'
def handle(self, *args, **kwargs):
users = User.objects.all()
for user in users:
try:
if user.email:
print("Sending email for to this email:", user.email)
form = PasswordResetForm({'email': user.email})
assert form.is_valid()
request = HttpRequest()
request.META['SERVER_NAME'] = 'www.example.com'
request.META['SERVER_PORT'] = 443
form.save(
request= request,
use_https=True,
from_email="admin#mysite.com",
email_template_name='registration/password_reset_email.html'
)
except Exception as e:
print(e)
continue
return 'done'
This can then be run easily with python manage.py send_password_reset_emails.
Note that I got an error message (550, b'5.7.1 Relaying denied'), which ended up being an issue with the way that my localhost email server was set up, but once that was cleared this worked quite well. Having it in a management command is also nice because it doesn't clog up code that you use on a regular basis (views).
If you are reading this with django 3.x or 4.x AND are using django-allauth, this will work for you:
from allauth.account.forms import ResetPasswordForm # note the different form import
form = ResetPasswordForm({"email": "some#mail.io"})
assert form.is_valid() # required because we need clean() executed
form.save(
request=None,
from_email="noreply#example.io",
)
Please note that this will take all values from the allauth config, using django.contrib.sites.