Django & SQLite.db - data is duplicated - python

I created 2 models in the Django framework. The first model is responsible to save emails and the second model to save messages. All emails and messages are saved in the SQLite.db. But when I add the same emails multiple times, the data base creates a new record and I don't have a clue how can I manage saving data to retrieve multiple emails with the same name and then pass them as a one mutual email to the HTML template with all assigned messages to them.
An example:
I sent 3 messages from test#test.com. Messages: ['Hi', 'Hello', 'Bonjour'] and one message from user#user.com ['Hi']
DB table:
Actual result:
3 records
test#test.com | 'Hi'
test#test.com | 'Hello'
test#test.com | 'Bonjour'
user#user.com | 'Hi'
Model:
class Email(models.Model):
"""The guest's email."""
text = models.EmailField(max_length=100)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""Return a string representation of the model."""
return self.text
class EmailMessage(models.Model):
"""The guest's message."""
email = models.ForeignKey(Email, on_delete=models.CASCADE)
message = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.message
Then I want to pass all data to the HTML template in order to display them:
url: /emails
def emails(request):
"""Show all emails."""
emails = Email.objects.order_by('date_added')
context = {'emails': emails}
return render(request, 'home/emails.html', context)
HTML portion:
(I want to display only emails on this page, without messages, and when I click on each email, the user should be redirected to another page that display all messages assigned to a particular email.)
<h1>Emails</h1>
<ul>
{% for email in emails %}
<li>
{{ email.text }}
</li>
{% empty %}
<li>No emails have benn added yet.</li>
{% endfor %}
</ul>
url: /emails/email_id
def email(request, email_id):
"""Show a single email and all its messages."""
email = Email.objects.get(id=email_id)
messages = email.emailmessage_set.order_by('-date_added')
context = {'email': email, 'messages': messages}
return render(request, 'home/messages.html', context)
Template:
<h1>Email: {{ email }}</h1>
<ul>
{% for message in messages %}
<li>
<p>{{ message.date_added|date:'M d, Y H:i' }}</p>
<p>{{ message|linebreaks }}</p>
</li>
{% empty %}
<li>No emails have benn added yet.</li>
{% endfor %}
</ul>
But the final result is:
test#test.com
message_1: Hi
test#test.com
message_1: Hello
test#test.com
message_1: Bonjour
user#user.com
message_1: Hi
Expected result:
test#test.com
message_1: Hi
message_2: Hello
message_3: Bonjour
user#user.com
message_1: Hi
!!! Messages are available when I click on a particular email! So when User click on a particular email, he will be redirected to another page with all messages.
The question is how to handle it? And should I modify the HTML (javascript), view function or created models? Which approach is the best to make my page more stable?

We can pass the EmailMessages with:
def emails(request):
messages = EmailMessage.objects.select_related('email').order_by('date_added')
context = {'messages': messages}
return render(request, 'home/emails.html', context)
You can {% regroup … %} [Django-doc] the messages by email address:
<h1>Emails</h1>
<ul>
{% regroup messages by email.text as message_group %}
{% for email, messages in message_group %}
<li>
{{ email }}:
<ul>
{% for message in messages %}
{{ message.message }}
{% endfor %}
</ul>
</li>
{% empty %}
<li>No emails have been added yet.</li>
{% endfor %}
</ul>

Related

Flask WTForms Validate on Submit not working

I am working on a page in my application where a user submits a review on a review page using WTForms, and upon clicking the submit button, the text "Success" should display. This is currently not happening, as upon clicking submit, the review page simply reloads itself but with an error.
I have created other pages requiring form validation which has worked, except for this one and I can't seem to figure out why, despite replicating the code from other pages.
Any insights are much appreciated!
Screenshot of error image
Here is my HTML code
<html>
<body>
{% for books in books %}
{{books.title}}
{% endfor %}
<form action = "{{ url_for('review', isbn=books.isbn)}}", method = "POST">
<div>
<br>{{ form.review(class_='form-control',placeholder ='Leave a review here')}}</br>
<ul>
{% for error in form.review.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
<br>{{ form.rating(class_='form-control',placeholder ='Leave a rating here')}}</br>
<ul>
{% for error in form.rating.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{{form.csrf_token}}
{{form.submit_button }}
</div>
</form>
</body>
</html>
Python Code
#app.route("/review/<string:isbn>", methods = ['GET', 'POST'])
#login_required
def review(isbn):
review = Books.query.filter_by(isbn=isbn).all()
review_form = ReviewForm()
if review_form.validate_on_submit():
return "Success"
return render_template("review.html", books = review, form = review_form)
WTForms fields
class ReviewForm(FlaskForm):
""" Review """
review = StringField('review_label', widget=TextArea(), validators = [InputRequired(message = "Review can't be empty")])
rating = StringField('rating_label', validators= [InputRequired(message="Please input a rating")])
submit_button = SubmitField('Submit')
I would suggest the following. Let me know if this helps! :)
from markupsafe import escape
#app.route("/review/<string:isbn>", methods = ['GET', 'POST'])
#login_required
def review(isbn):
review = Books.query.filter_by(isbn=isbn).all()
review_form = ReviewForm()
if review_form.validate_on_submit():
return "Success %s" % escape(isbn)
return render_template("review.html", books = review, form = review_form)
reference flask documentation

How to get count of friends of friends

i am building a website like instagram where users can follow friends, i have been able to implement follow friend and also displaying friends of friends (mutual friend). I was not able to get the count of friends of friends; this is what i tried:
Model:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,blank=True,null=True)
friends = models.ManyToManyField('Profile', related_name="my_friends",blank=True)
view:
#login_required
def profile_user_view(request, username):
#Friend of Friends
p = Profile.objects.filter(user__username=username).order_by('-id')
all_friends = request.user.profile.friends.values_list('pk', flat=True)
friends_of_friend = Profile.objects.filter(pk__in=all_friends)
context = {
'profile_img': p,
'friends_of_friend': friends_of_friend,
}
return render(...)
Template:
{% for data in profile_img %}
{% for friend in friends_of_friend %}
{% if friend in data.friends.all %}
<li>
<a href="{% url 'site:profile-view' friend.user.username %}" class="dark-grey-text">
<b>{{ friend.user.username|lower }}</b>
</a>
</li>
<li>
{{ friend.count }} #This is not showing the count of friends of friends, when i use length it displays '0 0' instead of '2' (mutual friends)
</li>
{% endif %}
{% endfor %}
{% endfor %}
You need to use friends_of_friend.count to get count of current user's friends. If you want every friend's friend count then use {{ friend.friends.count }}.
Honestly, you do not need this much context in template. You can access them all from {{ user }} attribute in template. For example:
{% for friend in user.friends.all %}
Username {{ friend.user.username | lower }}
Count {{ friend.friends.count }}
{% endfor %}
i know it is late but ....
utils.py:
from django.contrib.auth.models import User
from collections import Counter
#this function finds friends_of_friends_id
def friendship(user):
a = User.objects.get(id=user)
return [i.id for i in a.profile.friends.all()]
#this function will take return all your friends's id with how many mitual_friend you have together
def count_mitual_friends(user_id):
return Counter([foaf_id for friend_id in friendship(user_id)
for foaf_id in friendship(friend_id)
if foaf_id != user_id
and foaf_id not in friendship(user_id)
])

Posts not showing author who wrote it in Django

Basically, I'm writing an app in which people can make blog and image posts. So far, I've completed users to be able to write text posts. However, when I try to create a post, it returns "By: None" when it should be returning "By: shrey". In this case, Bob is the author. Here's an image:
Here's an image for the post creation view:
Theoretically, when I enter a post it should say who it was written by.
Here's the template for the create post:
{% extends "social/base.html" %}
{% load crispy_forms_tags %}
{% block content4 %}
<h1>Make Your Post</h1>
<p>Write a post / Share an image</p>
<br>
<div class="container">
<form method="post">
{% csrf_token %}
{{form|crispy}}
<button type="submit" name="button">Make Post</button>
</form>
</div>
{% endblock content4 %}
Here's the function for the create post view:
class PostCreateView(CreateView):
model = Posts
fields = ['post_title', 'post_text_content']
def form_valid(self, form):
form.instance.author = self.request.user
print(self.request.user)
return super().form_valid(form)
Thank you in advance.
EDIT: Home Page Template (template which displays the posts):
{% extends "social/base.html" %}
{% block content %}
<h1>Your Feed</h1>
<p>This is your feed. Here, you'll see posts from people you follow.</p>
{% if user.is_authenticated %}
<p>You are logged in as {{user.username}}. This is your feed.</p>
{% else %}
<p>You are not logged in. This is a random feed.</p>
{% endif %}
{% for post in posts %}
<h1>{{ post.post_title }}</h1>
<p>By {{ post.post_author }} on <i>{{ post.post_date }}</i></p>
<p>{{ post.post_text_content }}</p>
{% endfor %}
Click here to make a post.
<br>
Click here to logout.
<br>
Click here to login.
<br>
Click here to sign up and make an account.
<!--<p>Want to post something? Enter your info here: </p> -->
{% endblock content %}
Posts Model:
class Posts(models.Model):
post_title = models.CharField(max_length = 40, help_text = 'Enter post title')
post_text_content = models.TextField(max_length = 1000)
post_author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
post_date = models.DateField(auto_now = True, auto_now_add = False)
#Make optional Image Field
class Meta:
ordering = ['post_title', 'post_author', 'post_date', 'post_text_content']
def __str__(self):
return self.post_title
def get_absolute_url(self):
return reverse('social-home')
The name of the field is post_author, not author, hence you should set post_author:
class PostCreateView(CreateView):
model = Posts
fields = ['post_title', 'post_text_content']
def form_valid(self, form):
form.instance.post_author = self.request.user
return super().form_valid(form)
That being said, normally in Django one does not prefixes the model fields with the name of the model. One reason not to do that is that you can define abstract models where you define the field once, and then use inheritance to add the field to other models.

redirect url with primary key DJANGO

So I have a list with some recipes and I want the user to be able to click on a recipe and be redirected to a view with more details. In other words, I want every recipe to have its own url. I am trying this code as below, but it does not work, it redirects 404 side after I click on recipe.
My model:
class Recipe(models.Model):
recipe_name = models.CharField(max_length=250)
preparation = models.CharField(max_length=1000)
ingredients = models.ManyToManyField(Ingredient)
recipe_image = models.ImageField(upload_to='images/', default='')
def __str__(self):
return self.recipe_name
View:
def GenerallView(request):
lista1 = Recipe.objects.all()
return render(request, 'drinks/GenerallView.html', {'lista1': lista1})
def DetailView(request, pk):
lista = get_object_or_404(Recipe, pk=pk)
return render(request, 'drinks/DetailView.html', {'lista': lista})
Url:
path('generall_view', views.GenerallView, name='GenerallView'),
path('detail_view/<int:pk>', views.DetailView, name='DetailView'),
Templates:
generall view
<ul>
{% for drink in lista1 %}
<li>{{ drink.recipe_name }}</li>
{% empty %}
<li>No notes yet.</li>
{% endfor %}
</ul>
detail view
<h1>{{ drink.recipe.name }}</h1>
The name of the obbject is drink, not Recipe, so you should write this with {% url 'DetailView drink.pk %}, furthermore the name of your view is DetailView, not detail_view:
<ul>
{% for drink in lista1 %}
<li>{{ drink.recipe_name }}</li>
{% empty %}
<li>No notes yet.</li>
{% endfor %}
</ul>
In the DetailView you passed a variable named lista as well, so you should render this with:
<h1>{{ lista.recipe_name }}</h1>

Getting user by primary key in flask

I'm working with flask and have a html page that contains of user name(user.html) which took from table, Now How can I see more detail by clicking on each of users(which route to profile)?
I don't use login for app So I don't want to use g
app.py
# am I doing it right?
#app.route('/profile/<int:id>')
def profile(id=None):
detail = Contacts.query.get(id)
return render_template('profile.html', detail= detail , id=id)
user.html
{% extends "layout.html" %}
{% block content %}
<h2>show user</h2>
{% for contact in contact %}
# I got error when I click on each user name to see their 'profile'
#I guess because of id How can Solve it?
#error BuildError: ('profile', {}, None)
<strong>name:</strong><a href={{url_for('profile')}}>
{{ contact.name}}</a><br>
{% endfor %}
{% endblock %}
profile.html
{% extends "layout.html" %}
{% block content %}
<h2>show user profile</h2>
# how can I make it specific for each row of table(each user)?
{% for detail in Contacts %}
<strong>name:</strong> {{ detail.name}} <br>
<strong>email:</strong> {{ detail.email }} <br>
<strong>age:</strong> {{ detail.age}} <br>
<br>
{% endfor %}
{% endblock %}
model.py
class Contacts(db.Model):
__tablename__ = "Contacts"
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(50))
email = db.Column(db.String(50))
age = db.Column(db.Integer)
submit = SubmitField("Submit")
I noticed two things in your code:
# this
<a href={{url_for('profile')}}>
# should be
<a href={{url_for('profile', id=contact.id)}}>
# otherwise Flask can't find the route, because it needs an id
And the other one:
{% for detail in Contacts %}
There is no such thing as a Contacts variable in your template, because your view function does not send it. Just get rid of the loop and use detail directly, because it's what you sent to the template.

Categories