how can i do a feed of posts with flask and Jinja2? - python-3.x

I'm using the flask-SQLAchemy to manage my DB and i have a table named post with the columns content, title, date, likes and profile_id, in the feed i use a simple jinja2 template and i send a variable with all posts in db, when i render the posts i make a for loop to render it but i can't take the name of the person who make the post (i have a variable with his id); So my question is how can i take the name of user who make the post in a jinja template?

Is the column 'profile_id' the primary key? If so, then your post might be actually showing the ID of the post (i.e. the number that corresponds the primary key or the main column to identify a certain post) instead of what you want.
class Post(db.Model):
profile_id = db.Column(db.Integer, primary_key=True)
likes = db.Column(db.Integer, nullable=False)
title = db.Column(db.String(250))
date_posted = db.Column(db.DateTime)
content = db.Column(db.Text)
Try analyzing the code above, I assume that's your code, what what you need to do is add another column which in this case the column known as the 'author', so do the following:
class Post(db.Model):
profile_id = db.Column(db.Integer, primary_key=True)
likes = db.Column(db.Integer, nullable=False)
title = db.Column(db.String(250))
author = db.Column(db.String(250))
date_posted = db.Column(db.DateTime)
content = db.Column(db.Text)
Done? Try opening a terminal and build your new model by doing the following:
python name-of-your-app.py
from name-of-your-app import *
db.create_all()
exit()
By doing this, you are making sure that your new model is actually being used instead of the old one. The next thing for you to do is add the 'author' column to your add function and edit function, then make sure to add a new input for the 'author' column in your jinja2 template and also replace the 'profile_id' with 'author'. If you are still confused, please refer to the code I've used while I was still learning flask. Also, consult to the provided link which I've also used to make the provided code.
https://www.youtube.com/watch?v=XHGpPCYmPvI
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from flask_simplelogin import SimpleLogin, is_logged_in
from datetime import datetime
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///blog.db'
app.config['SECRET_KEY'] = 'something-secret'
app.config['SIMPLELOGIN_USERNAME'] = 'admin'
app.config['SIMPLELOGIN_PASSWORD'] = 'admin'
db = SQLAlchemy(app)
SimpleLogin(app)
class Blogpost(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(50))
subtitle = db.Column(db.String(50))
author = db.Column(db.String(20))
date_posted = db.Column(db.DateTime)
content = db.Column(db.Text)
#app.route('/')
def index():
posts = Blogpost.query.order_by(Blogpost.date_posted.desc()).all()
return render_template('index.html', posts=posts)
#app.route('/about')
def about():
return render_template('about.html')
#app.route('/add')
def add():
if is_logged_in():
return render_template('add.html')
else:
return redirect('/')
#app.route('/post/<int:post_id>')
def post(post_id):
post = Blogpost.query.filter_by(id=post_id).one()
return render_template('post.html', post=post)
#app.route('/edit/<int:post_id>')
def edit(post_id):
post = Blogpost.query.filter_by(id=post_id).one()
if is_logged_in():
return render_template('edit.html', post=post)
else:
return redirect('/')
#app.route('/addpost', methods=['POST'])
def addpost():
title = request.form['title']
subtitle = request.form['subtitle']
author = request.form['author']
content = request.form['content']
post = Blogpost(title=title, subtitle=subtitle, author=author, content=content, date_posted=datetime.now())
db.session.add(post)
db.session.commit()
return redirect(url_for('index'))
#app.route('/deletepost/<int:post_id>', methods=['POST'])
def deletepost(post_id):
post = Blogpost.query.get_or_404(post_id)
db.session.delete(post)
db.session.commit()
return redirect(url_for('index'))
#app.route('/editpost/<int:post_id>', methods=['POST'])
def editpost(post_id):
post = Blogpost.query.get_or_404(post_id)
post.title = request.form['title']
post.subtitle = request.form['subtitle']
post.author = request.form['author']
post.content = request.form['content']
db.session.commit()
return redirect('/')
if __name__ == "__main__":
app.run(debug=True)

Related

NOT NULL constraint failed: jobs_job.created_by_id IntegrityError at /jobs/add/

I am trying to enable the user to be able to add job and after filling the form i got that error. Here is my code:
models.py
from django.db import models
from django.contrib.auth.models import User
class Job(models.Model):
title = models.CharField(max_length=255)
short_description = models.TextField()
long_description = models.TextField(blank=True, null=True)
created_by = models.ForeignKey(User, related_name='jobs', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
changed_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
views.py
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .forms import AddJobForm
from .models import Job
def job_detail(request, job_id):
job = Job.objects.get(pk=job_id)
return render(request, 'jobs/job_detail.html', {'job': job})
#login_required
def add_job(request):
if request.method == 'POST':
form = AddJobForm(request.POST)
if form.is_valid():
job = form.save(commit=True)
job.created_by = request.user
job.save()
return redirect('dashboard')
else:
form = AddJobForm()
return render(request, 'jobs/add_job.html', {'form': form})
forms.py
from django import forms
from .models import Job
class AddJobForm(forms.ModelForm):
class Meta:
model = Job
fields = ['title','short_description','long_description']
What do i need to do to solve this error? I have try to remove the commit=True, from views.py and I have try to remove sqlite3 and do the migrations again.
You are trying to save the object without a value for created_by (hence the error message), then assign a value for that field and save again. You should change
job = form.save(commit=True)
to
job = form.save(commit=False)
so that the first .save doesn't actually try to commit anything to the database. It is simply used to construct the Job instance so that you can complete any missing values and actually save it using job.save().
Note that if you omit the commit= argument it is assumed to be True.

How to retrieve necessary fields from ModelForms and pass them to views.py properly?

Short background of whole idea: I'm trying to create landing page and for backend I use Python/Django3 framework. I need to create form with such fields as name, surname, email, mobile number and address of customer.
I've decided to save all the information that the user enters in the database, and also send it to my email. In order to connect database to forms I use ModelForms in forms.py section (all the required code will be shown below). In order to connect my email to form I use send_mail function from django.core.mail.
So when a user fills out his form, he is redirected to the 'thank you' page and all the information from the form is saved in the database and sent to my email.
But when I finally wrote the code and started to test functionality, I found out that when I hit 'submit' form button, error <type object 'ClientInfoForm' has no attribute 'cleaned_data'> is raised.
I suppose, that method cleaned_data is wrong in my case and I have to replace it with something else but I don't know with what exactly. All the code will be placed below and any help will be helpful. Thank you!
Models.py file
from django.db import models
class ClientInfo(models.Model):
name = models.CharField(max_length=264)
surname = models.CharField(max_length=264)
email = models.EmailField(max_length=264, unique=True)
mobile_number = models.CharField(max_length=20, unique=True)
address = models.CharField(max_length=264)
def __str__(self):
return f'Client: {self.name} {self.surname}'
Forms.py file
from django import forms
from landing_page.models import ClientInfo
class ClientInfoForm(forms.ModelForm):
class Meta():
model = ClientInfo
fields = '__all__'
Views.py file
from django.shortcuts import render
from landing_page.forms import ClientInfoForm
from django.core.mail import send_mail
def thanks(request):
return render(request, 'landing_page/thanks.html')
def index(request):
form = ClientInfoForm()
if request.method == 'POST':
form = ClientInfoForm(request.POST)
if form.is_valid():
form.save(commit=True)
name = ClientInfoForm.cleaned_data['name']
surname = ClientInfoForm.cleaned_data['surname']
email = ClientInfoForm.cleaned_data['email']
mobile_number = ClientInfoForm.cleaned_data['mobile_number']
address = ClientInfoForm.cleaned_data['address']
send_mail(
f'New client: {name} {surname}',
f'Client Name: {name}\nClient Surname: {surname}\nClient email: {email}\n'
f'Client Mobile Number: {mobile_number}\nClient address: {address}',
email,
['zombe#yandex.ru'],
)
return thanks(request)
else:
print('ERROR FORM INVALID')
return render(request, 'landing_page/index.html', {'form':form})
guys, in case you all interested I found out what was the actual problem. I just had to change this code below
name = ClientInfoForm.cleaned_data['name']
surname = ClientInfoForm.cleaned_data['surname']
email = ClientInfoForm.cleaned_data['email']
mobile_number = ClientInfoForm.cleaned_data['mobile_number']
address = ClientInfoForm.cleaned_data['address']
To this version
name = form.cleaned_data['name']
surname = form.cleaned_data['surname']
email = form.cleaned_data['email']
mobile_number = form.cleaned_data['mobile_number']
address = form.cleaned_data['address']

TypeError: contact() got an unexpected keyword argument 'name'

i was creating blog website using flask and flaskALacedemy and i got error please help me for this thank you
i was creating blog website using flask and flaskALacedemy and i got error please help me for this thank you
from flask import Flask,render_template,request
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app=Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] =
'mysql://root:#localhost/myfirstweb'
db = SQLAlchemy(app)
class Contact(db.Model):
serial_no = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20), unique=False, nullable=False)
email = db.Column(db.String(20), unique=False, nullable=False)
phone_no = db.Column(db.String(12), unique=False, nullable=False)
message = db.Column(db.String(120), unique=False, nullable=False)
date = db.Column(db.String(12), unique=False, nullable=True)
#app.route('/')
def home():
return render_template('index.html')
#app.route('/about')
def about():
return render_template('about.html')
#app.route('/contact', methods = ['GET', 'POST'])
def contact():
if(request.method=='POST'):
name = request.form.get('name')
email = request.form.get('email')
phone = request.form.get('phone')
message = request.form.get('message')
entry =
contact(name=name,email=email,phone_no=phone,date=datetime.now(),
message=message)
db.session.add(entry)
db.session.commit()
return render_template('contact.html')
#app.route('/post')
def post():
return render_template('post.html')
app.run(debug=True)
Your function contact doesn't expect any parameters and still you are still doing this: contact(name=name,email=email,phone_no=phone,date=datetime.now(), message=message)

How can a check on foreign key be enforced before insert a new value in Flask SQLAlchemy?

I am building the following DB scheme in Flask-SQLAlchemy:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
posts = db.relationship('Post', backref='author', lazy='dynamic')
def __repr__(self):
return '<User {}>'.format(self.username)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return '<Post {}>'.format(self.body)
db.drop_all()
db.create_all()
u = User(username='susan', email='susan#example.com')
db.session.add(u)
p = Post(body='this is my first post!', user_id=1)
db.session.add(p)
# Now I create a new post for a non-existing user and I'd expect an error...
p = Post(body='this is my second post!', user_id=2)
db.session.add(p)
db.session.commit()
As you can see I manually enter a new user and then enter two posts.
I would like that on the second post insert an error is thrown because user_id=2 is not existing in the database.
But instead the insert is successful and I also see the data into the DB:
sqlite> select * from post;
1|this is my post!|2018-07-09 16:13:16.947856|1
2|this is my post!|2018-07-09 16:13:16.948996|2
sqlite>
How can I enforce the constraint in order to make it throw an error?
Thanks for your help.
To resolve this situation, instead of assigning an id to a post object, it would be better and more ORM-like to associate a User with a Post. Also, by enforcing the NOT NULLability of the foreign key column, invalid numbers cannot be added.
For example:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
posts = db.relationship('Post', lazy='dynamic', back_populates='author')
def __repr__(self):
return '<User {}>'.format(self.username)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
author = db.relationship('User')
def __repr__(self):
return '<Post {}>'.format(self.body)
db.drop_all()
db.create_all()
# Susan will be both created and added to the session
u1 = User(username='susan', email='susan#example.com')
db.session.add(u1)
# John will be created, but not added
u2 = User(username='john', email='john#example.com')
# Create a post by Susan
p1 = Post(body='this is my post!', author=u1)
# Add susan's post to the session
db.session.add(p1)
# Create a post by john, since john does not yet exist as a user, he is created automatically
p2 = Post(body='this is my post!', author=u2)
# Add john's post to the session
db.session.add(p2)
# After the session has everything defined, commit it to the database
db.session.commit()

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table

I defined table name users_table and run db.create_all() to create the table, but get error "no such table user_table" on commit for updating user info.
How I test :
(under /project) python3 manage.py shell
>>> u = User(email='foo#bar.com', username='foobar', password='player')
>>> db.create_all()
>>> db.session.add(u)
>>> db.session.commit() # with following error message
Traceback (most recent call last):
File "C:\...\Python\Python36-32\lib\site-packages\sqlalchemy\engine\base.py", line 1182, in _execute_context
context)
File "C:\...\Python\Python36-32\lib\site-packages\sqlalchemy\engine\default.py", line 470, in do_execute
cursor.execute(statement, parameters)
sqlite3.OperationalError: no such table: users_table
...
...
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users_table
/project/app/_init_.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import config
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
db.init_app(app)
return app
/project/app/models.py:
import os
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash
from flask import Flask
basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
db = SQLAlchemy(app)
class User(db.Model):
__tablename__ = 'users_table'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
def __repr__(self):
return '<User %r>' % self.username
#property
def password(self):
raise AttributeError('Password is not a readable attribute')
#password.setter
def password(self, password):
self.password_hash = generate_password_hash(password)
project/config.py:
import os
basedir = os.path.abspath(os.path.dirname(\__file__))
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'fhuaioe7832of67^&*T#oy93'
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
#staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
config = {
'development': DevelopmentConfig,
'default': DevelopmentConfig,
}
project/manage.py:
import os
from app import create_app, db
from app.models import User
from flask_script import Manager, Shell
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)
def make_shell_context():
return dict(app=app, db=db, User=User)
manager.add_command("shell", Shell(make_context=make_shell_context))
if __name__ == '__main__':
manager.run()
I just got done setting up a Flask app and I dealt with this kind of problem.
I strongly suspect the problem here is that the instance of db that you are creating in __init__.py is unaware of the contents of models.py, including the User class. The db object in __init__.py is a totally separate object from the db you are creating in models.py. So when you run db.create_all() in __init__.py, it is checking the list of tables that it knows about and isn't finding any. I ran into this exact issue.
What I discovered is that the models (like User) are registered with the particular db object that is listed in the model's class definition (e.g. class User(db.Model):).
So basically my understanding is that the way to fix this is to run db.create_all() using the same instance of db that is being used to define the models. In other words, run db.create_all() from within models.py.
Here's my code so you can see how I have it set up:
app.py:
#!flask/bin/python
import os
from flask import Flask
class CustomFlask(Flask):
jinja_options = Flask.jinja_options.copy()
jinja_options.update(dict(
variable_start_string='%%', # Default is '{{', I'm changing this because Vue.js uses '{{' / '}}'
variable_end_string='%%',
))
app = CustomFlask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
import yaml
if os.environ['SERVER_ENVIRONMENT'] == 'PRODUCTION':
config_filename = "production.yaml"
elif os.environ['SERVER_ENVIRONMENT'] == 'LOCAL':
config_filename = "local.yaml"
else:
config_filename = "local.yaml"
base_directory = path = os.path.dirname(os.path.realpath(__file__))
with open(base_directory + "/config/" + config_filename) as config_file:
config = yaml.load(config_file)
db_config = config['database']
SQLALCHEMY_DATABASE_URI = "mysql+mysqlconnector://{username}:{password}#{hostname}/{databasename}".format(
username=db_config['username'],
password=db_config['password'],
hostname=db_config['hostname'],
databasename=db_config['databasename'],
)
app.config["SQLALCHEMY_DATABASE_URI"] = SQLALCHEMY_DATABASE_URI
app.config["SQLALCHEMY_POOL_RECYCLE"] = 299
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)
db.app = app
def clear_the_template_cache():
app.jinja_env.cache = {}
app.before_request(clear_the_template_cache)
from flask_login import LoginManager
login_manager = LoginManager()
login_manager.init_app(app)
#login_manager.user_loader
def load_user(email):
from models import User
return User.query.filter_by(email=email).first()
if __name__ == '__main__':
from routes import web_routes
app.register_blueprint(web_routes)
from api import api
app.register_blueprint(api)
# To get PyCharm's debugger to work, you need to have "debug=False, threaded=True"
#app.run(debug=False, threaded=True)
app.run(debug=True)
models.py:
from app import db
import datetime
from werkzeug.security import generate_password_hash, \
check_password_hash
class Song(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(80))
datetime_created = db.Column(db.DateTime, default=datetime.datetime.utcnow())
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
lines = db.relationship('Line', cascade="all,delete", backref=db.backref('song', lazy='joined'), lazy='dynamic')
is_deleted = db.Column(db.Boolean, default=False)
class Line(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
song_id = db.Column(db.Integer, db.ForeignKey('song.id'))
spans_of_time = db.relationship('SpanOfTime', cascade="all,delete", backref=db.backref('line', lazy='joined'), lazy='dynamic')
class SpanOfTime(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
line_id = db.Column(db.Integer, db.ForeignKey('line.id'))
starting_64th = db.Column(db.Integer) # I'm assuming the highest-granularity desired will be a 1/64th note-length.
length = db.Column(db.Integer) # I guess this'll be in 1/64th notes, so a 1/16th note will be '4'.
content = db.Column(db.String(80))
class User(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
email = db.Column(db.String(80), primary_key=True, unique=True)
display_name = db.Column(db.String(80), default="A Rhymecraft User")
password_hash = db.Column(db.String(200))
datetime_subscription_valid_until = db.Column(db.DateTime, default=datetime.datetime.utcnow() - datetime.timedelta(days=1))
datetime_joined = db.Column(db.DateTime, default=datetime.datetime.utcnow())
songs = db.relationship('Song', cascade="all,delete", backref=db.backref('user', lazy='joined'), lazy='dynamic')
def __init__(self, email, password):
self.email = email
self.set_password(password)
def __repr__(self):
return '<User %r>' % self.email
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
def is_authenticated(self):
return True
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return str(self.email)
def init_db():
db.create_all()
# Create a test user
new_user = User('a#a.com', 'aaaaaaaa')
new_user.display_name = 'Nathan'
db.session.add(new_user)
db.session.commit()
new_user.datetime_subscription_valid_until = datetime.datetime(2019, 1, 1)
db.session.commit()
if __name__ == '__main__':
init_db()
Very simple solution: in the app.py or main.py you can just add these lines of code for fixing this issue:
#app.before_first_request
def create_tables():
db.create_all()
In your case, require to add following code into __init__.py:
from models import User, Role
#app.shell_context_processor
def make_shell_context():
return dict(db=db, User=User, Role=Role)
then you do your previous works, it's all work.
I run into the same problem, after doing a YT tutorial. I solved it by adding this code at the end of my __init__.py
from .models import User, Post
with app.app_context():
db.create_all()
Sidenote: Most tutorials don't use the with app.app_context(): . I think there was a update in flask, which is why this is needed. This caused errors in my code and maybe it helps someone who reads this.
I would like to mention that it was the flask tutorial from "corey schafer" after "part 6 - user authentication", and the error appeared when i ran some tests. just in case anyone else is doing the exact same tutorial and therfore finds it easier to identify my answer as helpful. I am not mentioning the creater for advertisement. I hope this is ok.
Create a folder named "instance" in the root directory and move your database file to that folder.

Resources