0

I created a BlogPost model(working fine) and then a BlogInfo model that has a foreign key variable to the previous one.

I successfully stored data from the form I created based on my BlogInfo model but can't show them after refreshing the codes. For example, I have a blog that has its blog_validated_id as 46. Then when the users see that blog, they can click a button, comment on a form using my Blog Info model (that form will have a model that stores a column called blog_post_id that will be 46).

Now, after the users commented, I want them to be redirected to that same blog_page, except that there will be all the blog comments that have blog_post_id as 46.

The 2 paragraphs above are the ideal thing I want to achieve. However, I meet 2 problems:

-1st one:

Once I commented and submit the blog info comment form (form2 in the codes below), I get this error: AttributeError: 'BlogPost' object has no attribute 'post_id' My traceback then end at these codes, which is at the end of my BlogPost model(which is shown together with BlogInfo model down below):

    def __repr__(self):
        return f"Post ID: {self.post_id} -- Date:{self.date}---{self.problem_name}"

BlogPost and BlogInfo models:

class BlogPost(db.Model):
    __tablename__ = 'blog_post'
    users = db.relationship(User)

    blog_id = db.Column(db.Integer, primary_key=True)

    user_id = db.Column(db.Integer,db.ForeignKey('users.id'), nullable=False) #users.id  is taken from the tablename(users) and id in its table


    bloginfos2 = db.relationship('BlogInfo', backref="comment2", lazy=True)

    def __init__(self, user_id):

        self.user_id = user_id

    def __repr__(self):
        return f"Post ID: {self.post_id}"


class BlogInfo(db.Model):

    __tablename__ = 'blog_info'

    users=db.relationship(User)
    blog_post=db.relationship(BlogPost)

    blog_info_id = db.Column(db.Integer, primary_key=True)
    blog_post_id = db.Column(db.Integer, db.ForeignKey('blog_post.blog_id'), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    text = db.Column(db.Text, nullable=False)

    def __init__(self, blog_post_id, user_id, text):
        self.blog_post_id = blog_post_id
        self.user_id = user_id
        self.text = text

    def __repr__(self):
        return f"Blog info ID:{self.blog_info_id}--Blog id:{self.blog_post_id} -- Date:{self.date} -- {self.text}"

Blog Info comment form(the one that the users commented):

{% for post2 in comment_blogs.items %}
        <p><small class="">Posted on:{{ post2.date.strftime('%Y-%m-%d') }}</small></p>
          <p class="card-text ml-5">{{ post2.text }}</p>
    {% endfor %}

My views.py:

@blog_posts.route('/<int:blog_validated_id>', methods=['GET', 'POST']) 
def blog_view(blog_validated_id):
    blog_view = BlogPost.query.get_or_404(blog_validated_id)

    print(blog_validated_id)

    form2=BlogInfoForm()

    if form2.validate_on_submit():

        blog_comment_validated = BlogInfo(text=form2.text.data,
                                          user_id=current_user.id,
                                          blog_post_id=blog_validated_id)
        db.session.add(blog_comment_validated)
        db.session.commit()
        flash("Blog's comment added")

        page = request.args.get('page', 1, type=int)
        comment_blogs1 = BlogInfo.query.filter(BlogInfo.blog_post_id.ilike(blog_validated_id)).order_by(BlogInfo.date.desc())
        comment_blogs = comment_blogs1.paginate(page=page,per_page=3)

        return redirect(url_for('blog_posts.blog_view', 
                           post=blog_view,
                           form2=form2,comment_blogs=comment_blogs, comment_text=blog_comment_validated.text, blog_validated_id=blog_validated_id))


    page = request.args.get('page', 1, type=int)
    comment_blogs = BlogInfo.query.order_by(BlogInfo.date.desc()).paginate(page=page, per_page=3)
    return render_template('blog_view.html', 
                           post=blog_view,
                          form2=form2, comment_blogs=comment_blogs, blog_validated_id=blog_validated_id)

If you wonder how I got blog_validated_id, please look at my 2st problem (it will be fast and straightaway)

-2st one:

I think that the codes above (which is now referenced below) is not working. After I redirect url_for (blog_posts.blog_view), my html file still show all the blog info comments that has blog_post_id different to blog_validated_id .I think that my python codes is not saving blog_validated_id:

 comment_blogs1 = BlogInfo.query.filter(BlogInfo.blog_post_id.ilike(blog_validated_id)).order_by(BlogInfo.date.desc())

Very importantly, blog_validated_id is a variable I passed from a HTML file to the python's blog_posts.blog view codes above.

The HTML file that leads to blog_posts.blog view python codes:

{% for post in many_posts.items%}
<a href="{{ url_for('blog_posts.blog_view', blog_validated_id=post.blog_id) }}">Readmore</a>
{% endfor %}

The many_posts variable above is all the blogs and blog_validated_id is the id of each individuals blog.

I'm a beginner in coding and I'm struggling with the 2 problems above. I would greatly appreciate it if you could help me.

Thank you.

gelonida
  • 5,327
  • 2
  • 23
  • 41
Upchanges
  • 310
  • 2
  • 14
  • 2
    As a general rule you get best answers if you write the shortest piece of code, that allows to reproduce the problem. Perhaps this is why your question got down-voted. It's best you copy your whole project to a second directory and strip then everything off that is not essential to reproduce the problem. (E.g. remove all fields from models, that are not needed, ...) If you do this step by step and check between each step whether your problem still exists you often identify the line which is causing issues. It's a lot of code to read, let's hope we the problem can be identified. – gelonida Jun 14 '20 at 22:36
  • Thank you @gelonida! I will fix this question and keep that in mind for next time :) – Upchanges Jun 15 '20 at 00:20

1 Answers1

1

In the class BlogPost you write in __repr__:

        return f"Post ID: {self.post_id} -- Date:{self.date}---{self.problem_name}"

Shouldn't this be either

        return f"Post ID: {self.pk} -- Date:{self.date}---{self.problem_name}"

or

        return f"Post ID: {self.blog_id} -- Date:{self.date}---{self.problem_name}"

Other Comments 1:

The way you declare primary keys and foreign keys is a little unusual compared to the way that I'm normally doing it. (It's probably not the cause of your issue, but it makes in my opinion the code more complicated to read for a benefice, that I don't understand.

if you create a model then Django automatically adds a primary key and calls it id. I don't see a reason to change this behavior. People using Django are used to the fact, that Model.id is the primary key. They don't even have to kook at the declaration of your model. If you change its name, people don't understand until they look at your class definition. So I'd suggest to not explicitly declare a primary key. and just remove

blog_id = db.Column(db.Integer, primary_key=True)

from the declaration of BlogPost and

blog_info_id = db.Column(db.Integer, primary_key=True)

from the declaration of BlogInfo

If you do this because you work with a database that pre-dates your Django project, then I understand. Though I in a similar situation declared a model the Django way (and let django also decide the table name) and copied the data from original tables into the new ones. (This doesn't work of course if you want Django and your old software access the same tables)

To make a reference to a models own primary key just write self.pk or (if you don't explicitly declare a primary key self.id) (self.id is supposed to be faster. self.pk is a little cleaner as it works also if a primary key has been declared explicitly. Look at Django queries - id vs pk to form your own opinion)

Other comments 2 If you declare Foreign keys the way it is shown in the tutorials, then you would just write.

from django.db import models
class BlogInfo(models.Model):
    blog_post = models.ForeignKey(
    'BlogPost', 
    on_delete=models.CASCADE,  # change this to your desired behaviour 
    null=True)

You notice two things It's just called blog_post and not blog_post_id. So in python code if you have a BlogInfo entry (e.g. blog_info = BlogInfo.objects.all()[0])

Then you just write blog_info.blog_post blog_post and you have an instance of type BlogPost If you really want the id, just type blog_info.blog_post.id

It would of course change your queries instead of

BlogInfo.objects.get(blog_post_id=47)

you had to write

BlogInfo.objects.get(blog_post_id=47)  # double _ between blog_post and id

or

BlogInfo.objects.get(blog_post_pk=47)  # double _ between blog_post and pk
gelonida
  • 5,327
  • 2
  • 23
  • 41
  • Hi @gelonida! Thank you so much for helping me!! My problem was that I wrote {self.post_id} and forget to filter blog_validated_id in my render template python views.py :) Also, thank you so much for giving me a lot of advice outside of helping me with the problems. Wish all the best to you and have a nice day! – Upchanges Jun 15 '20 at 01:01
  • Hi @gelonida, thank you so much for helping me multiple times. I really appreciate your help. If it's possible, it would be very great if you could help me with a new problem: https://stackoverflow.com/q/62516834/13097721. Thank you! – Upchanges Jun 23 '20 at 09:36