Trouble with Flask / Jinja2 conditional statement - displaying sqlalchemy class - python

I've added a user profile page to my flask app. Everything is working except for one item:
I would like to display is the user's "Account created on" date. As I've added this functionality (and the logic to record a user's creation date upon registering) after some users have already been created, this column in certain user's tables will be empty.
I decided to work around this with a little humor by using a jinja2 if / else statement to check if the user has a user_created date or not.
If the user has a date, it will be displayed. If not, instead of a date it will replace it with 'since the dawn of time'.
The problem is my {% if user.user_created %} conditional logic seems to be pulling the sqlalchemy DateTime class type instead of None displaying as so:
Member since: <class 'sqlalchemy.sql.sqltypes.DateTime'>
If used this same pattern to check for the existence of other data without problems so I'm guessing it's related to this column containing db.Column(db.DateTime), but I'm not sure how to solve it.
Here is my Jinja template code:
<h1>User: {{ user.username }}</h1>
{% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %}
{% if user.user_created %}
<p>Member since: {{ user.user_created }}</p>
{% else %}
<p>Member since: The dawn of time</p>
{% endif %}
And here is the column from my sqlalchemy User model:
user_created = (db.DateTime)
Any help would be appreciated

Yep, after testing the realization in the comment above, that was in fact the issue.
I accidentally omitted the db.Column in my model. All is working now.

Related

Only Add Certain Fields in Variable - Python Function - Django - Views

I have Django model with CharFields 'flname1', 'date1', and 'time1'. My goal in my HTML is to have a {{ forloop }} that runs through only the 'date1' and 'time1' fields and displayed all of them. My Problem is that in my views file I can't find a way to create a python variable that only contains two of the three fields from one model. Ie tried a lot but what I'm trying to avoid is...
posts = DocPost.objects.all()
This puts all three fields into a variable and displays them in my for loop which I don't want. I've also tried a lot of work with filters and go things that other people on the internet had success with that didn't work for me like...
posts = DocPost.objects.filter('flname1').only('date1', 'time1')
This didn't work and didn't section off date1 and time1 and pack them away in a variable that I could loop through. Ive tried a lot more than this at this point to no prevail. Thank for any help.
There are two things you can do to only get certain fields in a query and iterate over them. The template for both is pretty much the same
First, you can use only() to generate a queryset where each object only has certain fields populated and all the rest are deferred
# View
context['posts'] = DocPost.objects.only('date1', 'time1')
# Template
{% for post in posts %}
{{ post.date1 }}
{{ post.time1 }}
{% endfor %}
Second, you can use values() to generate a queryset of dictionaries that only contain the fields specified
# View
context['posts'] = DocPost.objects.values('date1', 'time1')
# Template
{% for post in posts %}
{{ post.date1 }}
{{ post.time1 }}
{% endfor %}

CBV: How To Make A Drop Down Database Query Using Django FormView

What I would like to do is have a dropdown list containing objects from a database. When the user selects an object, I would like the information displayed on the page.
What I have been able to do is have the values shown in the dropdown list, but I have not been able to display the way I'd like. I've been trying to do so a certain way to hopefully be able to better control the look of the form.
models.py
class Vehicle(models.Model):
make = models.CharField(max_length=20)
model = models.CharField(max_length=20)
def __str__(self):
return self.make + ' ' + self.model
class Package(models.Model):
make_model = models.ForeignKey(
Vehicle, on_delete=models.CASCADE
)
name = models.CharField(max_length=20)
price = models.IntegerField()
views.py
class VehicleCompareView(FormView):
template_name = "main/somehtml.html"
form_class = forms.CompareForm
forms.py
objectlist = forms.ModelChoiceField(queryset=models.Package.objects.all() \
.order_by('make_model__make'))
html file
<form method="GET">
<select class="form-control">
<option value="0" selected disabled>Select Vehicle 1</option>
{% for q in form.objectlist %}
<option value="{{q.name}}">{{ q.make_model__make }}</option>
{% endfor %}
</select>
</form>
<div>
<p>Selected Vehicle: {{q.make_model__make}} {{q.make_model__model}}<br>
Price: ${{q.price}}
</p>
</div>
So let's start with what I have found that somewhat works from messing with the code.
If I use {{form}}, I get the package list, but it only displays the package name of course. If I attempt to use .values() in the query set in forms.py, it returns what I want, but in a dictionary(so it'll literally show 'make': 'someMake', 'model':'someModel', 'package:'somepackage') in the user dropdown list.
When I attempt to do the select/option way that I prefer to, after the for loop doing {{ q }} returns the same list of packages, but if I try to format what I want to see {{ q.make_model__make }} {{ q.make_model__model }} {{q.name}}, nothing shows except a dropdown of blanks(though the correct number of blanks).
There may be a better way than what I'm trying, but from a lot of searching and looking around on here I found my way to using a FormView with a ModelChoiceField query set. At the end of the day, I want a dropdown list to show Make Model Name from the database. When the user makes a selection, I would like to display the attributes on the page. Obviously I haven't even gotten to the issue of the selection part yet. From what I understand I'll need to use Ajax to have everything populate on selection. But that's a problem for another time. For now I'd just like the dropdown to display the way I'd like. Preferably using the select/option method so I can have control over the look rather than just rendering a generic form.
I solved my issue. I started reading all posts on Django query forms I could find, including those that didn't relate to my issue, to better understand exactly how everything works. Someone mentioned that the select statement is built inside the ModelForm. I then realized I was trying to force two different things, and why what I was doing didn't really make sense. I played with the code some more and here is what I did to solve my issue of displaying in the manner I wanted.
forms.py
objectlist = forms.queryset = models.Vehicle.objects.all().order_by('make')
html
<form>
<select class="form-control">
<option value="" selected disabled>Select Vehcile 1</option>
{% for q in form.objectlist %}
{% for b in q.package_set.all %}
<option value="">
{{ q.make }} {{ q.model }} {{ b.name }}
</option>
{% endfor %}
{% endfor %}
</select>
</form>
Using the nested for loop was the only way I could get the attributes of a related model to display inside the same select dropdown. The model Package has a foreign key called 'make_model' that refers to the model Vehicle. I wasn't able to call 'make_model__make' to display related vehicle names with the package in the dropdown list.
I'll leave my mistake up for anyone else having a similar issue to see what not to do in this situation along with what worked for me in the end.

Django templates - Conditionally displaying a button for logged in users

I have a page in my Django app that needs to do one of the following depending on the status of the logged in user in relation to a group (not a Django user group; something custom to my app) represented on the page:
If the user can join the group, display a link to join the group.
If the user is in the group, display a link to leave the group.
If the user can't join the group, don't display either link.
One way of doing this would be to create three templates (one with the join link, one with the leave link, and one with no link) and choose the appropriate one in the view. I feel like it may be overkill to have three different templates that will only differ in one line of code, so I have not gone that route as of yet.
Displaying the correct content for conditions 1 and 2 exclusively using a template is not possible, and if it were I do not think it would be advisable. Users to groups is a many-to-many relationship, and determining group membership requires passing a user to the group or passing a group to the user.
Since Django templates don't allow passing function arguments, I am trying to solve this by passing a context variable to the template using get_context_data.
def get_context_data(self, **kwargs):
context = super(NetworkDetails, self).get_context_data(**kwargs)
user = ???
context['in_group'] = user.in_group(context['group_detail'])
return context
If I do that, how can I get the currently logged in user within that method? If that isn't possible, where else can I get that information outside of the template? Is there an accepted method of doing something like this?
Thanks!
One way of doing this would be to create three templates (one with the
join link, one with the leave link, and one with no link) and choose
the appropriate one in the view
That's funny because if you can choose what template to include, you can just choose what html to display. Instead of:
{% if join_link %}
{% include 'join_link.html' %}
{% endif %}
{% if leave_link %}
{% include 'leave_link.html' %}
{% endif %}
{% if not join_link and not leave_link %}
you can't join
{% endif %}
You could just have:
{% if join_link %}
join
{% endif %}
{% if leave_link %}
leave
{% endif %}
{% if not join_link and not leave_link %}
you can't join
{% endif %}
So, I don't understand why you want to use template inclusion.
If I do that, how can I get the currently logged in user within that method?
self.request.user
self.request.user.is_authenticated() # return True if the user is logged in
You can determine the condition in your view and pass appropriate flag(s) to the template using context.
If there are multiple views/templates that need this info, you could implement custom context processor which can add this info in context and its available in each template.
Or If you have any OneToOne or any such relationship with User in your app, you can implement method in that model.
You can check if a user is logged in by checking permissions
https://docs.djangoproject.com/en/dev/topics/auth/ also you can do the similar for your other needs.

Django template check for empty when I have an if inside a for

I have the following code in my template:
{% for req in user.requests_made_set.all %}
{% if not req.is_published %}
{{ req }}
{% endif %}
{% empty %}
No requests
{% endfor %}
If there are some requests but none has the is_published = True then how could I output a message (like "No requests") ?? I'd only like to use Django templates and not do it in my view!
Thanks
Even if this might be possible to achieve in the template, I (and probably many other people) would advise against it. To achieve this, you basically need to find out whether there are any objects in the database matching some criteria. That is certainly not something that belongs into a template.
Templates are intended to be used to define how stuff is displayed. The task you're solving is determining what stuff to display. This definitely belongs in a view and not a template.
If you want to avoid placing it in a view just because you want the information to appear on each page, regardless of the view, consider using a context processor which would add the required information to your template context automatically, or writing a template tag that would solve this for you.

How to delete record in Sqlite

I'm learning Python with the tutorial app from Flask and I can't figure how to reference a certain row in the database. I m trying to delete a record by using the delete link in the template.I spent a whole day trying everything I could find and to really understand this. I know it's simple but I don't get it!
Here's the html template:
<ul class=entries>
{% for entry in entries %}
<li><h2>{{ entry.title }}
<h2>{{ entry.text|safe }}</h2>
{% if session.logged_in %}
Delete
{% endif %}
{% else %}
{% endfor %}
</ul>
As you can see, next to each entry there's a delete link.How am I supposed to reference the
right id in the database?
Here's the Python function I have so far:
#app.route('/delete')
def delete_entry():
if not session.get('logged_in'):
abort(401)
g.db.execute('delete from entries where id=(select max(id)from entries)')
g.db.commit()
flash('The entry was deleted')
return redirect(url_for('show_entries'))
What I have so far works but It's not what I want. Thanks in advance!
Sebastien the delete_entry function is deleting the last entry in the db
where id=(select max(id)from entries)
What you have to do is insert a parameter in the url for deletion like '/delete/:id' where the id is the id of the entry you want deleted, then change the template to generate the url as so:
Delete
And also change the db query that deletes the entry:
g.db.execute('delete from entries where id=' + id)
Edit1:
I checked the Flask documentation, in order to get variables into your routes you have to modify the route as follows:
#app.route('/delete/<int:entry_id>')
In this way you obtain the entry_id in your python function:
def delete_entry(entry_id):
g.db.execute('delete from entries where id=' + entry_id)
And finally you have to specify your entry id in the url_for function:
Delete

Categories

Resources