Flask-SQLAlchemy queries - sqlalchemy

I am having issues with a seemingly simple sqlalchemy query using Flask.
I have a table called Links and within that table there are columns called 'id', 'author_id', 'link', and 'group'. My models.py looks like this:
class Links(db.Model):
__tablename__='links'
id = db.Column(db.Integer, primary_key=True)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
link = db.Column(db.String, unique=False, nullable=True)
group = db.Column(db.String, unique=False, nullable=False)
def __init__(self, author_id=None, link=None, group=None):
self.author_id = author_id
self.link = link
self.group = group
def __repr__(self):
return'<Link %r>' %(self.link)
I would like to return the values of all groups associated with the user that is logged into the application. Here is my views.py file:
#app.route('/members/', methods=['GET','POST'])
#login_required
def members():
error=None
form = PostLink(request.form, csrf_enabled = False)
uid = session['user_id']
link = "NULL"
groups = Links.query.filter_by(author_id=uid).all()
if request.method=='POST':
if form.validate_on_submit():
new_group = Links(
uid,
form.group.data,
link,
)
try:
db.session.add(new_group)
db.session.commit()
flash("You have created a group!")
except IntegrityError:
error = 'That group did not work, maybe it already exists?'
else:
flash_errors(form)
return render_template('members.html', form=form, error=error, link = link, groups=groups)
And my 'members.html':
{% extends "base.html" %}
{% block content %}
<p>Add New Group: {{ form.group }}</p>
<input id="link" type="hidden" name="link" value= {{ link }}/>
<p><input type="submit" value="Request"></p>
</form>
<br/>
{% for group in groups %}
<li><p>
{{ group }}
</p></li>
{% endfor %}
{% endblock %}
Currently this is just returning a list of links and groups in an odd format:
<Link u'link2'>
<Link u'linky'>
<Link u'linkymaybe'>
<Link u'madeit'>
<Link u'BLAH'>
So the core of my question is how do I build a query using SQLAlchemy to display all groups associated with the logged in user (uid = session['user_id']) I am pretty new to Flask and this problem is becoming an issue as I have tried a number of filter_by and filter statements with no luck at all.
Thank you in advance!

It is displaying correctly the object "Link" returned by the query.
You need to format it in the template.
Thisi is link {{ group.link }} from author #{{ group.author_id }} in group named {{ group.group }}
Maybe you've chosen a bad name "group" when cycling on results in the template. It should be called link.

In the template, you can show the group name using {{ link.group }} instead of {{ link }}. The query is returning the whole object.

Related

Django ListView count all keys in a JSON variable

I would like to return in my Link.html the number of links contained in allLinks JSON variable.
So far I guess I misunderstand the use of get_context_data() and how to pass in context['CountLink'] the total count of links for each Post.
With the current code, I got:
Liste des recherches
terre : <QuerySet [<Post: terre>, <Post: océan>]> Links
océan : <QuerySet [<Post: terre>, <Post: océan>]> Links
models.py
class Post(models.Model):
title = models.CharField(max_length=255)
url = models.URLField(max_length=255)
allLinks = models.JSONField()
def __str__(self):
return self.title
views.py
class LinkView(ListView):
model = Post
template_name = 'link.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['CountLink'] = Post.objects.all()
return context
Link.html
{% for post in object_list %}
<li>
{{ post.title }} :
{{CountLink}} Links
</li>
{% endfor %}
Example of allLinks: {"0": "github.com/kubernetes/kubernetes/releases/tag/v1.26.0", "1": "kubernetes.io/docs/concepts/overview/what-is-kubernetes",}
Use the built-in length template filter:
{{ post.allLinks|length }} Links
You can use count()
In your case I think you want something like that, it will return count of Post model objects
context['CountLink'] = Post.objects.all().count()
For more info check this

django-filters form not showing(only the submit button does)

template
<form method="get">
{{ filter.form.as_p }}
<input type="submit" value="Press" />
</form>
{% for obj in filter.qs %}
{{ obj.WorkType }} - ${{ obj.desired_wage }}<br />
{% endfor %}
views
#login_required(login_url='login')
def ListingsPage(request):
review = Review.objects.all()
filter = Profilefilter(request.GET, queryset=Profile.objects.all())
context = {"profile":profile,"review":review,"filter":filter}
return render(request,"base/Listings.html",context)
filters.py
import django_filters
from .models import Profile
class Profilefilter(django_filters.FilterSet):
name = django_filters.CharFilter(lookup_expr='iexact')
class Meta:
model = Profile
fields = ['WorkType', 'gender']
urls.py
urlpatterns = [
path('Listings/', views.ProfileSearch, name='profile_search_bar'),
path('',views.hello,name="home"),
path('Listings/', views.ListingsPage,name="listings"),
It is supposed to be showing the filters but doesn't render anything, only the submit button shows up. I think it is something to do with passing the context, not sure tho
You need to add qs for query filter
def ListingsPage(request):
review = Review.objects.all()
filter = Profilefilter(request.GET, queryset=Profile.objects.all())
review = filter.qs
context = {"profile":profile,"review":review,"filter":filter}
return render(request,"base/Listings.html",context)

How can I convert django forms.py select items to a list in html?

I have a form with which I intend to capture student marks. I have an issue like I don't know the best way to put it so that I have student name and markfield beside it for all the students. calling {{ form }} brings the form with select dropdown items(not what I want). Specifying form fields do not populate anything.i.e
{% for field in form %}
{{ field.student }}
{{ field.marks }}
{% endfor %}
This is the form
class AddMarksForm(forms.ModelForm):
def __init__(self, school,klass,term,stream,year, *args, **kwargs):
super(AddMarksForm, self).__init__(*args, **kwargs)
self.fields['student'] = forms.ModelChoiceField(
queryset=Students.objects.filter(school=school,klass__name__in=klass,stream__name=stream[0]))
class Meta:
model = Marks
fields = ('student','marks')
This is how I tried the html rendering
<form method="post" enctype="multipart/form-data" action="{% url 'add_marks' %}">
{% csrf_token %}
{% if form %}
<table>
<tr>
<td>Student</td>
<td>Marks</td>
</tr>
{% for field in form %}
<tr>
<td>{{ field.student }}</td>
<td>{{ field.marks }}</td>
</tr>
</table>
{% endfor %}
<button class="btn btn-outline-primary">Save</button>
</form>
My models.
class Year(models.Model):
year = models.IntegerField(validators=[MinValueValidator(2018), MaxValueValidator(4000),])
year_term = models.ForeignKey('exams.Term',on_delete=models.SET_NULL,related_name='year_term', null=True,blank=True)
class Subject(models.Model):
name = models.CharField(max_length=50)
teacher = models.ForeignKey(TeacherData,on_delete=models.CASCADE)
students = models.ManyToManyField(Students)
class Term(models.Model):
year = models.ForeignKey(Year,on_delete=models.CASCADE)
name = models.CharField(max_length=30)
exam_term = models.ForeignKey('exams.Exam',on_delete=models.SET_NULL,null=True,blank=True,related_name='exam_term')
class Exam(models.Model):
school = models.ForeignKey(School,on_delete=models.CASCADE)
year = models.ForeignKey(Year,on_delete=models.SET_NULL, null=True)
term = models.ForeignKey(Term,on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=20)
klass = models.ManyToManyField("students.Klass", related_name='klass',null=True,blank=True)
class Marks(models.Model):
exam = models.ForeignKey(Exam,on_delete=models.SET_NULL,null=True,blank=True)
subject = models.ForeignKey(Subject,on_delete=models.SET_NULL,null=True,blank=True)
student = models.ForeignKey(Students,on_delete=models.SET_NULL,null=True,blank=True)
marks = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(100),] ,null=True,blank=True)
More models
class Klass(models.Model):
name = models.IntegerField(validators=[MinValueValidator(1), MaxValueValidator(4),], help_text='E.g 1,2,3, 4')
school = models.ForeignKey(School,on_delete=models.CASCADE)
exam = models.ForeignKey("exams.Exam",on_delete=models.CASCADE, related_name='exam',null=True,blank=True)
class Stream(models.Model):
name = models.CharField(max_length=50,help_text='Name of the stream')
klass = models.ForeignKey(Klass,on_delete=models.CASCADE,help_text='Choose a class the stream belongs to')
class Students(models.Model):
id = models.AutoField(primary_key=True)
#student_user = models.OneToOneField(User, on_delete=models.CASCADE,null=True,blank=True)
school = models.ForeignKey(School,on_delete=models.CASCADE,help_text='A school must have a name')
adm = models.IntegerField(unique=True,validators=[MinValueValidator(1), MaxValueValidator(1000000),],help_text="Student's admission number" )
name = models.CharField(max_length=200,help_text="Student's name")
kcpe = models.IntegerField(validators=[MinValueValidator(100), MaxValueValidator(500),], help_text='What the student scored in primary')
klass = models.ForeignKey(Klass, on_delete=models.CASCADE,null=True,help_text="Student's Class/Form")
stream = models.ForeignKey(Stream,on_delete=models.CASCADE,blank=True,null=True,help_text="Student's Stream")
gender = models.ForeignKey(Gender,on_delete=models.CASCADE,null=True,help_text="Student's Gender")
notes = models.TextField(blank=True,null=True,help_text='Anything to say about the student. Can always be edited later')
Here is my suggestion based on the discussion in question comments.
# models.py
class User(models.Model):
is_teacher = models.BoolField(default=False)
is_student = models.BoolField(default=False)
class Subject(models.Model):
taught_by = models.ForeignKey(User, on_delete=models.PROTECT, limit_choices_to={"is_teacher": True})
students = models.ManyToManyField(User, limit_choices_to={"is_student": True})
class Exam(models.Model):
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
class Marks(models.Model):
exam = models.ForeignKey(Exam, on_delete=models.CASCADE)
student = models.ForeignKey(User, on_delete=models.CASCADE, limit_choices_to={"is_student": True})
marks = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(100),], null=True, blank=True)
# no need for a subject field here, that is linked through the Exam object
class Meta:
unique_togetger = [('exam', 'student'),]
I have only written the important fields, you can add more fields that you need.
# views.py
class AddMarks(views.View):
def get(self, request, exam_id):
try:
exam = Exam.objects.get(id=exam_id)
except Exam.DoesNotExist:
# some kind of handler that returns (important!)
context = {
"students": exam.subject.students.all()
"current_marks": Marks.objects.filter(exam=exam)
}
return render("add_marks.html", context=context)
def post(self, request, exam_id):
try:
exam = Exam.objects.get(id=exam_id)
except Exam.DoesNotExist:
# some kind of handler that returns (important!)
for student in exam.subject.students.all():
marks = int(request.POST.get(f"marks_{student.username}", None))
if marks:
# if that particular marks field exists in POST and is filled, let's update (if it exists) or create a new marks object
marks_object = Marks.objects.filter(exam=exam, student=student).first() or Marks(exam=exam, student=student)
marks_object.marks = marks
marks_object.save()
return self.get(request, exam_id)
<!--add_marks.html-->
<form method="POST">
<table>
{% for student in students %}
<tr>
<td>{{ student.get_full_name }}</td>
<td><input type="number" name="marks_{{ student.username }}" /></td>
</tr>
{% endfor %}
{% if students %}
<tr><td colspan="2"><input type="submit" value="Save marks" /></td></tr>
{% else %}
<tr><td colspan="2">No student is taking this class</td></tr>
{% endif %}
</table>
</form>
{% if current_marks %}
<h3>Currently saved marks</h3>
<table>
{% for marks in current_marks %}
<tr>
  <td>{{ marks.student.get_full_name }}</td>
<td>{{ marks.marks }}</td>
</tr>
{% endfor %}
</table>
{% endfor %}
I did make a couple of changes. Most noticeable is the Student model, or rather the lack thereof. I have made the User flaggable as either student or teacher, allowing your students to log in and see their grades or perhaps sign up for classes.
Second, as mentioned in the code, there is no need for a subject field in the Marks model, as that data can be pulled from the Exam itself.
Lastly about what this does. It will create a form with a table in it, where each row will contain a field named marks_<username>. The field can then be retrieved in the post method handler. If the field is there and has a value, the view will either update existing, or create a new Marks model. If there are already any marks saved, they will be showed below in a separate table.
I have removed the form entirely as that is redundant in this case. If you really wanted to, somewhere else perhaps, this multiple-forms-on-one-page thing can be achieved by using Django's FormSet API. The documentation about that is [here][1]. That allows you to stack many forms of the same kind one after another, for editing many rows at once. But since we only really have one field, I find it easier to just handle that one field myself.
One last thing, I did not write any error checking and handling. That I will leave to you. Potential places that need treatment are the two except blocks, that need to return from the method so that the code below isn't executed. Some nice 404 page should suffice. Another place is the int() call around getting the POST contents. That might throw an exception if something non-integer is sent in. You should be able to handle that with a simple try-except block.

How do I use a for loop to reuse a django form in a template

After struggling with this issue for a while, I am hoping someone here can point me in a more productive direction.
I am trying to take an indeterminate number of variables in a database (obtained through a different template) and render them on a webpage, each variable with a simple data entry form, to be saved back to the database. Basically, it's a tracker for analysis. Say I want to track my daily sleep, running time, and calorie intake (the variables). I have those saved in a database as variables and want to call upon those variables and show them on a webpage with a daily entry form. I am using a "for" loop right now and it renders the way I want it to, with the variable name and the form, but it is only saving the last item in the variable list. How do I amend the code below such that when I hit the save button for each form rendeded, it saves the information for that variable (not just the last one rendered).
Below is the code. Any and all help would be infinitely appreciated.
Models...
class Variable(models.Model):
date_added = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(get_user_model(), default='', on_delete=models.CASCADE) # id the active user
ENTRY_TYPE_CHOICES = [
('numeric', 'enter a daily number'),
('scale', 'rate daily on a scale of 1-10'),
('binary', "enter daily, 'yes' or 'no' "),
]
variable = models.CharField(max_length=50, default='')
entry_type = models.CharField(max_length=50, choices=ENTRY_TYPE_CHOICES, default="numeric")
def __str__(self):
return self.variable
class DailyEntry(models.Model):
date = models.DateField(default=datetime.date.today)
# date_added = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(get_user_model(), default='', on_delete=models.CASCADE) # id the active user
variable_name = models.CharField(max_length=50, default='')
variable_id = models.SmallIntegerField(default=0000)
entry = models.FloatField(default=9999)
class Meta:
verbose_name_plural = 'Daily Entries'
def __str__(self):
return self.variable
Form...
class VariablesForm(forms.ModelForm):
class Meta:
model = Variable
fields = ['variable', 'entry_type' ]
labels = {'variable':'Dependent variable to track', 'entry_type': 'Type of measure'}
class DailyEntryForm(forms.ModelForm):
class Meta:
model = DailyEntry
fields = ['variable_name', 'variable_id', 'entry', 'date']
labels = {'entry': 'Daily entry', 'date': 'Date'}
widgets = {'variable_name': forms.HiddenInput(), 'variable_id': forms.HiddenInput()}
Views...
def daily_entry(request):
''' page to make daily entries '''
vars = Variable.objects.filter(id__gt = 0 )
if request.method != 'POST':
# No data submitted. GET submitted. Create a blank form
form = DailyEntryForm()
else:
#POST data submitted. Process data
form = DailyEntryForm(data=request.POST)
if form.is_valid():
data = form.save(commit=False)
data.created_by = request.user
data.save()
return HttpResponseRedirect(reverse('entry_new'))
context = {'form': form, 'vars': vars}
return render(request, 'entry_new.html', context)
and HTML...
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
{% for var in vars %}
<div>
<ul>
<h3>{{ var.variable }}</h3>
<form class="" action="" method="post">
{% csrf_token %}
{{ form|crispy }}
<input type="hidden" name="variable_id" value="{{ var.id }}" >
<input type="hidden" name="variable_name" value="{{ var.variable }}">
<input type="submit" name="" value="Save" />
</ul>
</div>
{% endfor %}
{% endblock content %}
Any help, well, helps...
Thanks!

HTML input textbox in Django admin.py filter

I would like to filter data in Django (admin.py) with text writen in HTML input textbox. I need to filter companies by city in which they are and list of all cities is too long. I would like to replace list of all cities in filter by one text input. I found something similar
here http://djangosnippets.org/snippets/2429/ but there are two problems:
author did not posted models.py, so it is difficuilt to change code for my needs (+ no comments)
there is used class UserFieldFilterSpec(RelatedFilterSpec): but I need to use AllValuesFilterSpec instead of RelatedFilterSpec (more in file django/contrib/admin/filterspecs.py), because list of towns are in the same class as comapny (there shoud by class of towns and they should be referencing to company by foreign key (ManyToMany relationship), but for some reasons it have to be done this way)
important part of models.py looks something like this
class Company(models.Model):
title = models.CharField(max_length=150,blank=False)
city = models.CharField(max_length=50,blank=True)
and something from admin.py
class CatalogAdmin(admin.ModelAdmin):
form = CatalogForm
list_display = ('title','city')
list_filter = ['city',]
So again, I need to:
1. instead of list od cities display one text input in Django filter
2. After inputing city neme in that text input, filter data by city (request for filtering can be sent with some submit button or through javascript)
Thank yoy for all posts.
In case anybody still need this. It is little hackish in template, but implemented without a piece of js.
filters.py:
from django.contrib.admin import ListFilter
from django.core.exceptions import ImproperlyConfigured
class SingleTextInputFilter(ListFilter):
"""
renders filter form with text input and submit button
"""
parameter_name = None
template = "admin/textinput_filter.html"
def __init__(self, request, params, model, model_admin):
super(SingleTextInputFilter, self).__init__(
request, params, model, model_admin)
if self.parameter_name is None:
raise ImproperlyConfigured(
"The list filter '%s' does not specify "
"a 'parameter_name'." % self.__class__.__name__)
if self.parameter_name in params:
value = params.pop(self.parameter_name)
self.used_parameters[self.parameter_name] = value
def value(self):
"""
Returns the value (in string format) provided in the request's
query string for this filter, if any. If the value wasn't provided then
returns None.
"""
return self.used_parameters.get(self.parameter_name, None)
def has_output(self):
return True
def expected_parameters(self):
"""
Returns the list of parameter names that are expected from the
request's query string and that will be used by this filter.
"""
return [self.parameter_name]
def choices(self, cl):
all_choice = {
'selected': self.value() is None,
'query_string': cl.get_query_string({}, [self.parameter_name]),
'display': _('All'),
}
return ({
'get_query': cl.params,
'current_value': self.value(),
'all_choice': all_choice,
'parameter_name': self.parameter_name
}, )
templates/admin/textinput_filter.html:
{% load i18n %}
<h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>
{#i for item, to be short in names#}
{% with choices.0 as i %}
<ul>
<li>
<form method="get">
<input type="search" name="{{ i.parameter_name }}" value="{{ i.current_value|default_if_none:"" }}"/>
{#create hidden inputs to preserve values from other filters and search field#}
{% for k, v in i.get_query.items %}
{% if not k == i.parameter_name %}
<input type="hidden" name="{{ k }}" value="{{ v }}">
{% endif %}
{% endfor %}
<input type="submit" value="{% trans 'apply' %}">
</form>
</li>
{#show "All" link to reset current filter#}
<li{% if i.all_choice.selected %} class="selected"{% endif %}>
<a href="{{ i.all_choice.query_string|iriencode }}">
{{ i.all_choice.display }}
</a>
</li>
</ul>
{% endwith %}
Then according to your models in admin.py:
class CatalogCityFilter(SingleTextInputFilter):
title = 'City'
parameter_name = 'city'
def queryset(self, request, queryset):
if self.value():
return queryset.filter(city__iexact=self.value())
class CatalogAdmin(admin.ModelAdmin):
form = CatalogForm
list_display = ('title','city')
list_filter = [CatalogCityFilter,]
Ready to use filter would look like this.
I'm running Django 1.10, 1.11 and r_black's solution didn't completely fit because Django was complaining that filter fields must inherit from 'FieldListFilter'.
So a simple change for the filter to inherit from FieldListFilter took care of Django complaining and not having to specify a new class for each field, both at the same time.
class SingleTextInputFilter(admin.FieldListFilter):
"""
renders filter form with text input and submit button
"""
parameter_name = None
template = "admin/textinput_filter.html"
def __init__(self, field, request, params, model, model_admin, field_path):
super().__init__(field, request, params, model, model_admin, field_path)
if self.parameter_name is None:
self.parameter_name = self.field.name
if self.parameter_name in params:
value = params.pop(self.parameter_name)
self.used_parameters[self.parameter_name] = value
def queryset(self, request, queryset):
if self.value():
return queryset.filter(imei__icontains=self.value())
def value(self):
"""
Returns the value (in string format) provided in the request's
query string for this filter, if any. If the value wasn't provided then
returns None.
"""
return self.used_parameters.get(self.parameter_name, None)
def has_output(self):
return True
def expected_parameters(self):
"""
Returns the list of parameter names that are expected from the
request's query string and that will be used by this filter.
"""
return [self.parameter_name]
def choices(self, cl):
all_choice = {
'selected': self.value() is None,
'query_string': cl.get_query_string({}, [self.parameter_name]),
'display': _('All'),
}
return ({
'get_query': cl.params,
'current_value': self.value(),
'all_choice': all_choice,
'parameter_name': self.parameter_name
}, )
templates/admin/textinput_filter.html (unchanged):
{% load i18n %}
<h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>
{#i for item, to be short in names#}
{% with choices.0 as i %}
<ul>
<li>
<form method="get">
<input type="search" name="{{ i.parameter_name }}" value="{{ i.current_value|default_if_none:"" }}"/>
{#create hidden inputs to preserve values from other filters and search field#}
{% for k, v in i.get_query.items %}
{% if not k == i.parameter_name %}
<input type="hidden" name="{{ k }}" value="{{ v }}">
{% endif %}
{% endfor %}
<input type="submit" value="{% trans 'apply' %}">
</form>
</li>
{#show "All" link to reset current filter#}
<li{% if i.all_choice.selected %} class="selected"{% endif %}>
<a href="{{ i.all_choice.query_string|iriencode }}">
{{ i.all_choice.display }}
</a>
</li>
</ul>
{% endwith %}
Usage:
class MyAdmin(admin.ModelAdmin):
list_display = [your fields]
list_filter = [('field 1', SingleTextInputFilter), ('field 2', SingleTextInputFilter), further fields]
While it's not actually your question, this sounds like a perfect solution for Django-Selectables you can with just a few lines add an AJAX powered CharField Form that will have it's entries selected from the list of cities. Take a look at the samples listed in the link above.
Below is the fix for field name..in queryset function
class SingleTextInputFilter(admin.FieldListFilter):
"""
renders filter form with text input and submit button
"""
parameter_name = None
template = "admin/textinput_filter.html"
def __init__(self, field, request, params, model, model_admin, field_path):
super().__init__(field, request, params, model, model_admin, field_path)
if self.parameter_name is None:
self.parameter_name = self.field.name
if self.parameter_name in params:
value = params.pop(self.parameter_name)
self.used_parameters[self.parameter_name] = value
def queryset(self, request, queryset):
variable_column = self.parameter_name
search_type = 'icontains'
filter = variable_column + '__' + search_type
if self.value():
return queryset.filter(**{filter: self.value()})
def value(self):
"""
Returns the value (in string format) provided in the request's
query string for this filter, if any. If the value wasn't provided then
returns None.
"""
return self.used_parameters.get(self.parameter_name, None)
def has_output(self):
return True
def expected_parameters(self):
"""
Returns the list of parameter names that are expected from the
request's query string and that will be used by this filter.
"""
return [self.parameter_name]
def choices(self, cl):
all_choice = {
'selected': self.value() is None,
'query_string': cl.get_query_string({}, [self.parameter_name]),
'display': ('All'),
}
return ({
'get_query': cl.params,
'current_value': self.value(),
'all_choice': all_choice,
'parameter_name': self.parameter_name
}, )