Django: Quizapp with Question and Answer Model - python

I would like to create a Quiz app with Django.
Where the Questions can be stored in a DB and more users can add more questions in Admin.
and each question can have an answer from the user input.
This is a basic version of what I tried so far,
Simple example of My Models: 
QuestionModel
ID
question
author
AnswerModel
ID
Answer
question_id
author
So, When I create an AnswerForm():
it shows the form, but the question shows up as a dropdown instead of labels.
and it is not creating fields for each question. It just creates one input field and a dropdown for the question.
I know it does that because I have question_id as FK in the Answer Model.
Is there a better way to get this done?
I am new to Django
      
Here is a screenshot of what I am expecting

I am not good in django, but I think you can use these structure:
Question Model:
class Question(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=60,)
created_at = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(unique=True, max_length=200)
Answer Model:
class Answer(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
answer = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
post = models.ForeignKey(Question, on_delete=models.CASCADE)
In your views add:
class My_Answer(LoginRequiredMixin, CreateView):
model = Answer
fields = ['answer']
template_name = 'answer.html'
success_url = reverse_lazy('#Redirecting User To The Dashboard')
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.post_id = self.kwargs['pk']
result = super().form_valid(form)
return result
In your Urls add:
path('question/<int:pk>/answer/', views.My_Answer.as_view(), name='answer'),
Add this to your answer template:
{% load crispy_forms_tags %}
<form method="POST" action="" enctype="multipart/form-data">
{% csrf_token %}
{{ form | crispy }}
<input type="submit" value="submit" class="btn btn-primary">

Related

Django how to connect user profile model with comment model for showing data from user profile?

I want to show user profile picture publicly in my blog comment section. I tried to use foreignkey in my comment model for connect user profile model then use this in my html for showing profile picture but didn't work.
<img src="{{blogcomment.userprofile.profile_pic.url}}"> #didn't show any profile picture until I manually go to admin panel and set foreignkey of userprofile in my blogcomment model.
here is my full code:
userprofile model
class UserProfile(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,related_name="userprofile")
slug = models.SlugField(max_length=2000,unique=True,blank=True,null=True)
profile_pic = models.ImageField(upload_to='profile/images/',validators=[validate_file_size,FileExtensionValidator( ['png','jpg'] )],blank=True,null=True)
blogcomment model:
class BlogComment(models.Model):
blog = models.ForeignKey(Blog,on_delete=models.CASCADE,null=True, blank=True,related_name="blogcomment_blog")
comment = models.TextField(max_length=50000)
name = models.CharField(max_length=250)
userprofile= models.ForeignKey(UserProfile,on_delete=models.CASCADE,null=True,blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='user_comment',blank=True,null=True)
views.py:
if comment_form.is_valid():
isinstance = comment_form.save(commit=False)
isinstance.user = request.user
isinstance.blog = blog
isinstance.save()
my html template:
{% for q in queryset %}
{{q.user.first_name}}
{{q.comment}}
<img src="{{q.userprofile.profile_pic.url}}">
{%endfor%}
my froms.py
class CommentFrom(forms.ModelForm):
captcha = CaptchaField()
class Meta:
model = BlogComment
fields = ['name','email','comment','parent','sno','blog','user']
my userprofile forms.py
class ProfileFroms(forms.ModelForm):
class Meta:
model = UserProfile
fields = ["profile_pic","mobile","country","website_link","skype","twitter"]
userprofile views.py
class UserProfileUpdate(UpdateView):
model = UserProfile
form_class = ProfileFroms
template_name = 'members/profileupdate.html'
success_url = reverse_lazy('members:user-profile-private')
html template for saving userprofile forms
<form method="POST" enctype="multipart/form-data" runat="server">
{% csrf_token %}
{{form}}
</form>
Finally I solved my problems. As Willem Van Onsem said I am missing somethings in my froms. I need to be save userprofile forignkey with my comment model when any new comment posted. I am using this queryset UserProfile.objects.filter(user=request.user) for find current id then pass this id in forms.
{%for i in user_profile%}
<input type="hidden" name='userprofile' value="{{i.id}}">
{%endfor%}

Django Forum App, comments don't update on user-side, but can be seen through admin

For reference, here are my models in my Forum app:
class Forum(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('forum-detail', kwargs={'pk': self.pk})
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
forum = models.ForeignKey(Forum, on_delete=models.CASCADE)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
To display the forum posts, I have a CBV 'ForumListView':
class ForumListView(ListView):
model = Forum
template_name = 'forum/forum.html'
context_object_name = 'forum_posts'
ordering = ['-created_at']
From this list, the user can click on any forum and it will lead them to 'forum-detail' with the CBV 'ForumDetailView':
class ForumDetailView(DetailView):
model = Forum
extra_context = {
'comments': Comment.objects.all().order_by('-created_at')}
Here is where I passed in the comments from my Comment model to be shown alongside the post. I think this is the reason why the comments don't update, but I'm not too sure how to fix this.
In the template for forum_detail.html, this is how I display all the comments made:
{% for comment in comments %}
{% if comment.forum == forum %}
<div class="content-section">
<p>{{ comment.description }}</p>
<small>{{ comment.user.username }}, on {{ comment.created_at|date:"F d, Y" }}</small>
</div>
{% endif %}
{% endfor %}
Note that the new comment made will be shown if I re-run
python manage.py runserver
and sometimes the new comment appears after a few minutes of waiting/refreshing the page.
Also, I think function-based views may fix this, however I have coded my entire app with CBVs and would love a fix that doesn't involve re-coding everything!
Any help is greatly appreciated, and can provide more info/code if needed!
Putting it in extra_context like this will cause the queryset to be evaluated when you define the view, and any values that it has at that time will be the only values that the view will get. That's why it is working when you restart the server. So it should be dynamic and fetched every time a new request comes. In that case you need to put it in get_context_data
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['comments'] = Comment.objects.all().order_by('-created_at')}
return context

NOT NULL constraint failed: posts_comment.post_id django

I have the basics of a blogs app (I have been following Corey Schafer's tutorial, part 10, if that matters) and I am trying to post and display comments on the post that other users have wrote that only relate to that post. Similar to that of a comment section on Facebook or YouTube.
Currently I'm displaying all comments in the database on every post because I don't know how to filter the results to only show comments for the specific post and I also don't know how to post comments to a specific post, that's where I get the error.
My code is below:
Models.py
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
date_posted = models.DateTimeField(default=timezone.now)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('core:post-detail', kwargs={'pk': self.pk})
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.user.username
Views.py
class PostDetailView(DetailView):
model = Post
context_object_name = 'posts'
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['comment'] = Comment.objects.all()
return context
class CommentCreateView(LoginRequiredMixin, CreateView):
model = Comment
fields = ['content']
def form_valid(self, form):
form.instance.user = self.request.user
return super().form_valid(form)
urls.py (This is the urls.py file in my main app where I'm calling the blogs app views)
path('posts/<int:pk>/', PostDetailView.as_view(), name='post-detail'),
path('posts/<int:pk>/comment', CommentCreateView.as_view(), name='comment-create'),
I then have this form in a file called comment_form.html that I can connect to when I got to http://127.0.0.1:8000/posts/5/comment but when I click reply I get
IntegrityError at /posts/5/comment NOT NULL constraint failed: posts_comment.post_id
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4"> Reply to Post </legend>
{{ form|crispy}}
</fieldset>
<div class="form-group">.
<button type="submit" class="btn btn-success">Reply</button>
</div>
</form>
If someone could throw their eye over it and let me know if you can find anything that I could do to get it to work, I would greatly appreciate it. Let me know if you need more information/code.
Thanks
the problem is that you're creation form is missing a non nullable field, that's why django is complaining about the "NOT NULL CONSTRAINT"
so you have to add the post field to the form fields, or you can add it in the form_valid method:
def form_valid(self, form):
form.instance.post = Post.objects.get(pk=self.kwargs.get("pk"))
form.instance.user = self.request.user
return super().form_valid(form)
if you want to ignore the post field, you can put null=True in the Comment Model:
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post,null=True,blank=True,on_delete=models.CASCADE)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)

How to return multiple items in a get_queryset function in Django?

So I have a web app where the user can enter their information, and eventually I want to display all of it, but at the moment this code right here
class UserPostListView(ListView):
model = Post
template_name = 'mainapp/user_posts.html'
context_object_name = 'posts'
def get_queryset(self):
user = get_object_or_404(User,username=self.kwargs.get('username'))
first_name = get_object_or_404(User,first_name=self.kwargs.get('first_name'))
return Post.objects.filter(author=user).order_by('-published_date')
It gives me an error, and it says User not found.
I have tried add this to the end of the return statement
.order_by('-published_date'),first_name
However this did not work.
This is the relevant urls.py file responsible for the user posts
path('user/<str:username>', UserPostListView.as_view(), name='user-posts'),
This is the UserProfileInfo model
class UserProfileInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=50,blank=True,null=True)
last_name = models.CharField(max_length=50,blank=True,null=True)
description = models.CharField(max_length=150)
image = ProcessedImageField(upload_to='profile_pics',
processors=[ResizeToFill(150, 150)],
default='default.jpg',
format='JPEG',
options={'quality': 60})
joined_date = models.DateTimeField(blank=True,null=True,default=timezone.now)
verified = models.BooleanField( default=False)
def __str__(self):
return f'{self.user.username} Profile'
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
And here is the relevant bit of the user-posts.html
<div class="sidebar">
<p class="active" href="#">{{ view.kwargs.username }}</p>
<button class="commentbtn"><a class="aclass" href="#">Connect with {{ view.kwargs.username }}</a></button>
<p>{{ view.kwargs.first_name }}</p>
<p>Lorem</p>
</div>
I want to be able to display the first name of the person in the ```{{ view.kwargs.first_name }}, however everything I have tried has failed to work
I expected no errors to occur and for this to work, however this is not the case. Is it possible to have 2 get_queryset methods, or is there something I can do in the current one to achieve my goal of displaying the information of the user
Thanks for any help you can give :)
How about this?
def get_queryset(self):
qs = super().get_queryset() #this is the same as Post.objects.all()
user = self.request.user
return qs.objects.filter(author=user).order_by('-published_date')
Now you can access this query using object_list on your template, but since you changed it's name with context_object_name='posts', use posts instead:
{% for post in posts %}
<h1>{{ post.first_name }}</h1>
...
{% endfor %}
But it looks like the Post model isn't going to have a first_name column?
You might be better off using the request.user object here as well:
<h1>{{ request.user.first_name }}</h1>
<h2>{{ request.user.username }}</h2>

Django access foreignkey fields in a form

I'm working on a Virtual Library app (using Django v2.1, python v3.5) where anyone should be able to access the book catalog and request a loan by simply leaving some personal info like name, surname, email, etc.
These are some of the models in models.py:
class Profile(models.Model):
name = models.CharField(max_length=50)
# more fields like surname, email, phone...
class TrackBook(models.Model):
# Somefields to keep track of date and status...
borrower = models.ForeignKey(Profile, on_delete=models.SET_NULL, null=True, blank=True)
class Book(TrackBook):
#info about title, author, etc.
What I'm trying to do is to update a Book instance's borrower with a Profile instance that I created in the Form.
1)I've tried directly accessing borrower fields in a BookForm, but it didn't work.
# views.py
class BookRequestView(UpdateView):
template_name = 'core/book_request.html'
model = Book
form_class = BookProfileForm
#forms.py
class BookProfileForm(forms.ModelForm):
class Meta:
model = Book
fields = ['borrower']
# book_request.html
<form class="" action="" method="post">
{% csrf_token %}
<div class="row">
{{ form.borrower.name }}
<! -- and all other fields -->
</div>
{% for field in form.hidden_fields %}
{{ field }}
{% endfor %}
<button type="submit" class="btn btn-block btn-success btn-flat">Save</button>
</form>
2) I've tried creating an inlineformset_factory() of Profile model but it doesn't work since what I want to achieve is create a profile form inside the book form, not viceversa. So the example here is not what I'm looking for.
Maybe I'm going out of my mind for a very simple thing, but I can't seem to find any compatible solution for this problem... Any help/suggestion is welcome. Thanks in advance.
You need a form based on Profile, not Book. Your view then needs to create the profile and then set the book's borrower to that.
class BookProfileForm(forms.ModelForm):
book = forms.ModelChoiceField(queryset=Book.objects.all())
class Meta:
model = Profile
fields = ['name', 'address',...]
class BookRequestView(CreateView):
template_name = 'core/book_request.html'
model = Book
form_class = BookProfileForm
def form_valid(self, form):
borrower = form.save()
book = Book.objects.get(self.kwargs['book_id'] # or whatever is in your URL
book.borrower = borrower
book.save()
return redirect(self.get_success_url())

Categories