Testing if logged-in users see unpublished questions - python

EDIT
Here is my test:
def test_admin_sees_unpublished_questions(self):
"""
Logged-in admin users see unpublished questions on the index page.
"""
# create admin user and log her in
password = 'password'
my_admin = User.objects.create_superuser('myuser', 'myemail#test.com', password)
user = authenticate(username="myuser", password="password")
if user is not None:
print(user.username)
else:
print('Not found!')
self.client.login(username=my_admin.username, password=password)
#create future question and check she can see it
create_question(question_text="Unpublished question.", days=5)
response = self.client.get(reverse('polls:index'))
self.assertContains('Please review these unpublished questions:')
self.assertEqual(response.context["user"], user)
self.assertQuerysetEqual(
response.context['unpublished_question_list'],
['<Question: Unpublished question.>']
)
It's a bit messy. There are a few lines checking if there is a user in context which all appear to show response.context["user"] is there.
Here is my view:
class IndexView(generic.ListView):
template_name = 'polls/index.html'
def get_queryset(self):
"""
Return the last five published questions (not including those set to be
published in the future).
"""
queryset = Question.objects.filter(
pub_date__lte=timezone.now()
).exclude(
#question without choices
choice=None
).order_by('-pub_date')[:5]
return queryset
def get_context_data(self, **kwargs):
"""
Override get_context_data to add another variable to the context.
"""
context = super(IndexView, self).get_context_data(**kwargs)
context['unpublished_question_list'] = Question.objects.filter(pub_date__gte=timezone.now())
print(context)
return context
I am writing tests for the django polls app tutorial.
I want to write a test which which logs in a user, creates an instance of the question model with a publish date in the future, and ensures that this logged in user can see this question.
I've tried using
self.assertContains('Please review these unpublished questions: ')
in the test method because my template looks like this:
{% if user.is_authenticated %}
<p>Hello, {{ user.username }}. Please review these unpublished questions: </p>
{% if unpublished_question_list %} etc
but even though
self.assertEqual(response.context["user"], user)
passes testing after
self.client.login(username=my_admin.username, password=password)
my template doesn't seem to be rendering properly to the test client.
Some help would be much appreciated!
AssertionError: False is not true : Couldn't find 'Please review these unpublished questions: ' in response

Related

How to create a conversation list in Django chatting app

I am trying to add messaging functionality to my web app made in Django.
So far, I have managed to successfully send and receive messages from user to user.
But, now I have been stuck at showing all the conversation lists to the inbox.html page of the logged user.
I have tried different approaches that I can think of but can not get the expected result.
models.py
class Messaging(models.Model):
sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sender')
receiver = models.ForeignKey(User, on_delete=models.CASCADE, related_name='receiver')
message_text = models.TextField(max_length=360, verbose_name='Write Message')
message_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.sender}\'s Message to {self.receiver}'
viwes.py
Function to send and receive messages from user to user
#login_required
def messageview(request, user_name):
sender_user = request.user
receiver_user = User.objects.get(username=user_name)
message_list = Messaging.objects.filter(sender=sender_user, receiver=receiver_user).order_by('message_date') | \
Messaging.objects.filter(receiver=sender_user, sender=receiver_user).order_by('message_date')
if request.method == 'POST':
msg_text = request.POST.get('msg_text')
messaging = Messaging()
messaging.sender = sender_user
messaging.receiver = receiver_user
messaging.message_text = msg_text
messaging.save()
return redirect(request.META['HTTP_REFERER'])
context = {
'sender_user': sender_user,
'receiver_user': receiver_user,
'message_list': message_list,
}
return render(request, 'message.html', context)
Now I want to create an inboxview in views.py that will render all the conversation of the logged user.
Suppose I have two users in the database A and B, they have exchange 4 messages between them. What I want is to show the conversation as a list, which is in this case only one. For example, the logged user is A, he exchanges messages with user B and C. The inbox will show two rows. When user A clicks on either of the rows, he will be taken to the details message page corresponding to the user. It is kinds of like WhatsApp or messenger. I hope I can explain.
Edited: Added example image for better understanding
I am able to do this:
I need help to do this:
Please guide me the way.
You could try something like this.
This view will query the Messaging model and get all entries where sender or receiver is the logged in user.
#login_required
def inbox(request, user):
# Get all the records where either sender OR receiver is the logged in user
messages = Messaging.objects.filter(sender=request.user) | Messaging.objects.filter(receiver=request.user)
context = {'messages': messages}
return render(request, 'inbox.html', context)
You can add any extra lines of code to the above code that suits your requirements.
inbox.html
{% for message in messages %}
# You can show your message details here
{% endfor %}

Django/Python - Help moving is_authenticated and user == model.user to view

First off, sorry if I am not clear on what I'm trying to do or if I don't give enough info, I'm still relatively new to Django/Python.
I currently have a view that renders something like a blog post:
class SingleCharacter(LoginRequiredMixin,generic.DetailView):
model = models.Character
def get_context_data(self, **kwargs):
context = super(SingleCharacter, self).get_context_data(**kwargs)
return context
And, in the template for this view, I have a template tag that checks if the user is authenticated AND the owner of the post:
{% if user.is_authenticated and user == character.user %}
However, I'm currently in the process of incorporating xhtml2pdf into my project and this method of securing the post to only the user who created it is causing some issues.
I'm wondering if it's possible to move the user.is_authenticated and user == character.user into the view instead of a template tag, and if so can i do it with a simple if statment, something like this?
class SingleCharacter(LoginRequiredMixin,generic.DetailView):
model = models.Character
if user.is_authenticated and user == character.user:
def get_context_data(self, **kwargs):
context = super(SingleCharacter, self).get_context_data(**kwargs)
return context
else:
<I'll include some redirect to a 404 page>
I'm trying to see if there's another way to do it, but I thought I'd throw this out here to the more experienced people in hopes of figuring it out.
Thanks for any help!
You can override the get method of the DetailView and handle the logic there:
class SingleCharacter(LoginRequiredMixin,generic.DetailView):
model = models.Character
def get(self, request, *args, **kwargs):
self.object = self.get_object()
if self.request.user.is_authenticated and self.request.user == self.object.user:
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
else:
# redirect

django login users and display their instance and items in the database

I have couple of models and one of them is a foreign key to the user model that's extending django admin. I want to display what belongs to a user in their session upon login. I have defined this authentication that will check whether a particular user exist within the database and redirect them to their session with their instances.
def auth_view(request):
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = auth.authenticate(username=username, password=password)
if user is not None:
auth.login(request, user)
return HttpResponseRedirect('/studentloggedin/')
Basically, Registration is the first model and a foreign key to Student model, while Student is also a foreign key to UserLog. UserLog is extending the default django admin. I've defined the loggedin session here to filter out details of the individual users upon login.
def studentloggedin(request):
registration = Registration.objects.all()
students = Student.objects.filter(registration=registration)
alluser = UserLog.objects.filter(student=students)
context = {
'registration': registration,
'students': students,
'alluser': alluser,
}
return render(request, "studentloggedin.html", context)
Here is the template rendering the information upon login.
<img
{% for student in students %}
src="{{ student.student_photo.url }}">
<p>{{ student.previous_school }}</p>
{% endfor %}
But I'm getting the below error:
ProgrammingError at /studentloggedin/
more than one row returned by a subquery used as an expression
I think your re doing something that already comes with django:
if request.user.is_authenticated:
# Do something for authenticated users.
...
else:
# Do something for anonymous users.
...
from here:https://docs.djangoproject.com/en/1.10/topics/auth/default/#authentication-in-web-requests
you can also use decorator #login_required

How to remove session variable in a template after it's job is done in django

I have a app called dashboard which is where I redirect all logged in users with an option to add articles by the user.
After the user hits Submit button in the form, the data is sent to /dashboard/article/save URL via POST and after the data is stored, the view returns HttpResponseRedirect to show_dashboard which renders dashboard.html with a session variable result.
In the dashboard template file, I have added a notify.js code to show acknowledgements to user. The problem is if this session var is defined, everytime the dashboard page is showed, the notification is triggered EVEN if the user didn't add an article.
(I'm new to using web frameworks so I do not know how this all works properly)
Some code:
dashboard/models.py:
class Article(models.Model):
id = models.IntegerField(primary_key=True)
ar_title = models.CharField(max_length=25)
ar_data = models.CharField(max_length=500)
user = models.ForeignKey(User,on_delete=models.CASCADE)
def getArticleTitle(self):
return self.title
def getArticleData(self):
return self.title
def getArticleAuthor(self):
return self.user
dashboard/urls.py:
urlpatterns = [
url(r'^$', views.show_dashboard,name='home_dashboard'),
url(r'^profile/save/', views.save_profile,name="save_profile"),
url(r'^newsfeed/', views.get_newsfeed,name="newsfeed",),
url(r'^profile/', views.show_profile,name="show_profile"),
url(r'^article/save/', views.add_new_article,name="add_new_article"),
]
dashboard/views.py:
#login_required
def show_dashboard(request):
return render(request,'dashboard/dashboard.html',{'form':NewArticleForm()})
def add_new_article(request):
if(request.method == 'POST'):
ar_title= request.POST['ar_title']
ar_data = request.POST['ar_data']
user = request.user
form = NewArticleForm(request.POST)
if(form.is_valid()):
Article.objects.create(ar_title=ar_title,ar_data=ar_data,user=user)
request.session["result"] = "add_article_OK"
return HttpResponseRedirect(reverse('home_dashboard'))
dashboard.html:
{% ifequal request.session.result 'add_article_OK' %}
<script>
$.notify("New article added successfully",
{position:"bottom right","className":"success","autoHide":"yes","autoHideDelay":"3000"});
</script>
{% endifequal %}
Now, how do I remove this session value after it has displayed the message? I know del request.session['result'] can be issued but where can I put it in this heirarchy of moves?
Do it in the show_dashboard view.
Instead of getting the value from the session in the template, pop it in the view and pass it to the template; that way you take care of getting and clearing it in one go.
#login_required
def show_dashboard(request):
context = {
'form': NewArticleForm(),
'result': request.session.pop('result', None)
}
return render(request,'dashboard/dashboard.html',context)
...
{% ifequal result 'add_article_OK' %}

Django notification email to users in ManyToMany relation

I want to send notification emails to a list of users defined in a 'observers' ManyToMany field when a new post is created.
The post is created without errors and the list of observer users are added to it successfully (they appear in the post_detail.html template), but the notification email is never sent to the observer users.
I think I'm doing something wrong in the new_post function below, which I adapted from this code for sending email when a user comments on a post, which does work. Any help much appreciated.
models.py (relevant parts):
from django.db import models
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.db.models import signals
from notification import models as notification
class Post(models.Model):
author = models.ForeignKey(User, related_name="added_posts")
observers = models.ManyToManyField(User, verbose_name=_("Observers"), related_name='observers+', blank=True, null=True)
# send notification to Post observers
def create_notice_types(app, created_models, verbosity, **kwargs):
notification.create_notice_type("new_post", "New post created", "A new post has been created")
signals.post_syncdb.connect(create_notice_types, sender=notification)
def new_post(sender, instance, created, **kwargs):
context = {
'observer': instance,
'site': Site.objects.get_current(),
}
recipients = []
pk=instance._get_pk_val()
for observer in instance.observers.all().distinct():
if observer.user not in recipients:
recipients.append(observer.user)
notification.send(recipients, 'new_post', context)
signals.post_save.connect(
new_post, sender=models.get_model(
'blog', 'Post'), dispatch_uid="pkobservers")
views.py (relevant parts):
#login_required
def add(request, form_class=PostForm, template_name="blog/post_add.html"):
post_form = form_class(request)
if request.method == "POST" and post_form.is_valid():
post = post_form.save(commit=False)
post.author = request.user
post_form.save()
post_form.save_m2m()
return redirect("blog_user_post_detail",
username=request.user.username, slug=post.slug)
return render_to_response(template_name,
{"post_form": post_form}, context_instance=RequestContext(request))
EDIT:
Also tried this (after dropping the blog_post and blog_post_observers tables, and running manage.py syncdb again, but still doesn't work):
models.py
class Post(models.Model):
# ....
observers = models.ManyToManyField(User, verbose_name=_("Observers"), related_name='observers+')
def new_post(sender, instance, created, **kwargs):
context = {
'observer': instance,
'site': Site.objects.get_current(),
}
recipients = instance.observers.all()
pk=instance._get_pk_val()
notification.send(recipients, 'new_post', context)
signals.post_save.connect(new_post, sender=models.get_model('blog', 'Post'), dispatch_uid="pkobservers")
EDIT 2 Thursday, 27 June 2013: 11:48:49 Italy Time:
When I edit/update a post, using the following view, the notification email does work:
views.py
#login_required
def edit(request, id, form_class=PostForm, template_name="blog/post_edit.html"):
post = get_object_or_404(Post, id=id)
if post.author != request.user:
request.user.message_set.create(message="You can't edit items that aren't yours")
return redirect("desk")
post_form = form_class(request, instance=post)
if request.method == "POST" and post_form.is_valid():
post = post_form.save(commit=False)
post.updated_at = datetime.now()
post_form.save()
post_form.save_m2m()
messages.add_message(request, messages.SUCCESS, message=_("Successfully updated post '%s'") % post.title)
return redirect("blog_user_post_detail", username=request.user.username, slug=post.slug)
return render_to_response(template_name, {"post_form": post_form, "post": post}, context_instance=RequestContext(request))
First of all, I think your many to many relationship should not have blank or null, since each observer would ideally be a user and not a None. This avoids trying to send emails to None users, which probably gives error.
Second, I think you can just use
recipients = instance.observers.all().distinct()
instead of looping on the list (distinct() already considers only unique users)
Third, I don't see why you really need a "distinct()": can a user be observer more than once?
recipients = instance.observers.all()
Fourth, In your code, you are looping trough instance.observers.all() with an "observer", which is already a User. Why do you append observer.user in recipients? I think appending observer should be enough.
Finally, confirm that recipients is not empty. If you have tested notification.send(), your code seems correct.
Figured it out — the notification email is now sent to observers when creating a new post.
Basically I needed to call save a second time after setting the post.id:
views.py:
#login_required
def add(request, form_class=PostForm, template_name="blog/post_add.html"):
post_form = form_class(request)
if request.method == "POST" and post_form.is_valid():
post = post_form.save(commit=False)
post.author = request.user
post_form.save()
post.id = post.id # set post id
post_form.save() # save a second time for notifications to be sent to observers
return redirect("blog_user_post_detail",
username=request.user.username, slug=post.slug)
return render_to_response(template_name,
{"post_form": post_form}, context_instance=RequestContext(request))
The models.py is unchanged from the first EDIT: in the question (Thanks to J. C. Leitão for the help).
Here it is to be sure:
models.py
class Post(models.Model):
observers = models.ManyToManyField(User, verbose_name=_("Observers"), related_name='observers+')
def new_post(sender, instance, created, **kwargs):
context = {
'observer': instance,
'site': Site.objects.get_current(),
}
recipients = instance.observers.all()
pk=instance._get_pk_val()
notification.send(recipients, 'new_post', context)
signals.post_save.connect(new_post, sender=models.get_model('blog', 'Post'), dispatch_uid="pkobservers")

Categories