Can't call model to Django template - html

I'm trying to call an attribute from my model into my HTML template using Django. There is something strange going on as I am only able to call one of my two models into the template. Both models are working perfectly fine as far as I can tell by looking into my database. This is what my models.py looks like
class Respondez(models.Model):
responder = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='scores')
score = models.IntegerField(default=0)
post_time = models.DateTimeField(default=timezone.now)
class Meta:
ordering = ['post_time']
def __str__(self):
return self.score
class Profilez(models.Model):
newuser = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True,null=True)
preference = models.CharField(max_length=30)
def __str__(self):
return self.newuser
I am trying to call Profilez. However, only Respondez can be called. This is the view I'm calling from, which I simplified.
#login_required
def add(request):
p = Profilez()
z = Respondez()
context = {
'p' : p,
'z' : z
}
return render(request, 'rating/add.html', context)
To test whether I can call my models, I have simple header tags in HTML for my template, add.html:
{% extends "rating/base.html" %}
{% block content%}
<h3> {{user.username}} </h3>
<h3> {{ z.post_time }}</h3>
<h3>{{ p.preference }}</h3>
No matter which attribute I call from the models, the line for Respondez works but nothing works for my Profilez model. This is despite the fact that my database has values saved for each attribute from both models.
I am getting inputs for preference from the following view on a separate template (first line won't paste with correct indentation), where users select 1 of 2 choices:
def onboarding2(request):
p = Prof()
p.newuser = request.user
if request.method == 'POST':
selected_opt = (request.POST['ob'])
if selected_opt == 'mood':
p.preference = 'mood'
elif selected_opt == 'productivity':
p.preference = 'productivity'
else:
return HttpResponse(400, 'Invalid form')
p.save()
return redirect('rating-onboarding3')
context = {
'p' : p,
}
return render(request, 'rating/onboard2.html', context)
How can I accurately call my Profilez model? What's wrong here?

Since you have instantiated Profilez with no parameters in the constructor, none of its fields get the initial value. Hence, p.preference also happens to be null. That is why p.preference is not visible in the template.
But, in case of Respondez, though you are still instantiating the object with no parameters, you still have given the default value of current time to z.post_time, so z.post_time is working.
If you want to access p.preference, you need to explicitly assign some value to p.preference, else how will the template show the value for something that doesn't have a value initialized in the first place? For instance, you could do p = Profilez(preference='xyz') while creating the object, and see what happens.
Also, if you want to fetch a specific entry from the database, then you need to do a query, rather than creating a new object. The syntax for creating query would be something like Profilez.objects.get(newuser=some_random_user).

Related

How to get value attribute in views

Hello is there a way to get 'value' attribute from HTML template into views.py and use it there??
HTML:
<form class="card__delete" method="POST"> {% csrf_token %}
<button value="{{item.id}}" class="card__delete__button" name="delete" type="submit">&#10008</button>
</form>
views.py
class TodoView(UserPassesTestMixin, CreateView):
model = Item
template_name = 'home/todo.html'
form_class = ItemCreationForm
def test_func(self):
return self.request.user.username in self.request.path
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['card'] = Card.objects.get(user=self.request.user, pk=self.kwargs.get('pk'))
return context
def post(self, request, pk, username):
if 'delete' in self.request.POST:
Item.objects.get(id=pk).delete()
print('deleted')
return redirect('home-page')
The value is in request.POST, so you should be able to access it with
value = self.request.POST.get('delete', None)
Take care to validate that value before using the id of an object to do anything catastrophic to it (such as .delete()). It's not being validated through a form, and a random hacker on the internet might try posting back random numbers which might be the id of other objects
Added after reading comment:
Data pulled out of request.POST is raw data. I don't think CSRF token can protect against a person who uses inspect object in his browser and changes the value of that button before clicking it. I may be wrong.
Anyway, if you can check the value using a queryset of the object type with a filter for objects that this user is permitted to delete, then do. For example,
value = request.POST.get("delete", None)
if value:
obj = Card.objects.filter(
user=self.request.user ).get( pk=value)
# will raise CardDoesNotExist if value isn't one of user's objects,
# because it's been filtered out
obj.delete()

Validation erorr raise for the form that validates its value from another model

I am trying to raise validation error for the entry field in the forms.py
My models.py
class StudBackground(models.Model):
stud_name=models.CharField(max_length=200)
class Student(models.Model):
name=models.CharField(max_length=200)
My forms.py
class StudentForm(forms.ModelForm):
name = forms.CharField(max_length=150, label='',widget= forms.TextInput)
class Meta:
model = Student
fields = ['name',]
where i tried to apply clean method :
def clean_student(self,*args,**kwargs):
name=self.cleaned_data.get("name")
if not studBackground.stud_name in name:
raise forms.ValidationError ( "It is a not valid student")
else: return name
I tried to incorporate stud_name from the StudBackground model to the form but it does not work it raises following error when i try to type student name that is not in DB:
Profiles matching query does not exist
however it supposed to return near the name field "It is a not valid student"
How to make it work? What is the wrong with the code?
You can try like this:
def clean_student(self):
name=self.cleaned_data.get("name")
if not StudBackground.objects.filter(stud_name=name).exists():
raise forms.ValidationError("It is a not valid student")
return name
I am using filter(...) function from queryset to check if a name exists in StudBackground. I am also running exists() to check if entry exists in DB.
Update
I think your indentations are not correct for the view. But, you can try like this:
def home(request):
form = StudentForm(request.POST or None)
if request.method == "POST":
if form.is_valid():
instance = form.save()
name = instance.name
class_background=StudBackground.objects.get(stud_name=name)
context={'back':class_background}
return render(request, 'class10/background.html', context)
# Now let us handle if request type is GET or the form is not validated for some reason
# Sending the form instance to template where student form is rendered. If form is not validated, then form.errors should render the errors.
# How to show form error: https://docs.djangoproject.com/en/3.0/topics/forms/#rendering-form-error-messages
return render(request, 'your_student_form_template.html', context={'form':form})

Django-Filter Form Displaying All Filters?

I've just started working with Django Filter. When I test, the filter.form shows filters for all fields and I can't get it to show only the desired filters.
Here's the filter:
class EmployeeFilter(django_filters.FilterSet):
hire_date = django_filters.DateFilter(name='hireDate', lookup_expr='hireDate__year')
hire_date__gte = django_filters.DateFilter(name='hireDate', lookup_expr='hireDate__gte')
hire_date__lte = django_filters.DateFilter(name='hireDate', lookup_expr='hireDate__lte')
class Meta:
model = models.Employee
fields=['hireDate']
Here's the view:
def test_filters(request, template_name='filter-test.html'):
from . import filters
f = filters.EmployeeFilter(request.GET, queryset=models.Employee.objects.all())
return render_to_response(template_name, locals(), context_instance=RequestContext(request))
Has anyone ever run into this? How'd you fix?
I had the same issue but I was able to solve
class EmployeeFilter(django_filters.FilterSet):
class Meta:
model = Employee
fields=['hireDate']
In Views.py
def filters(request):
employee = Employee.objects.all()
myFilter = EmpoyeeFilter(request.GET, queryset=employee)
employee = myFilter.qs
context = {
'myFilter': myFilter,
'employee': employee,
}
return render(request, 'templates/index.html', context)
index.html file
<html>
<form method="get">
{{myFilter.form}}
</form>
</html>
The filter set's form plays both of Django Form's two roles:
It enables you to display the form in your template
It validates the incoming values.
The second of these is (arguably) the more important — certainly for Django Filter: if you remove fields from the form they will not be filtered against.
Your best bet is probably to just define less fields on the filter set. (If you need all fields in some cases just define two filter sets, one for each need.)
(The other option would be to define a separate Django Form with just the fields you need and use that in your template, leaving the filter set's form to do the actual validation.)
I hope that helps.
models.py
class Employee(models.Model):
hire_date = models.DateField()
name = models.CharField(max_length=128)
filters.py
class EmployeeFilter(django_filters.FilterSet):
hire_date = django_filters.DateFilter(field_name='hire_date', lookup_expr='gt')
hire_date__gte = django_filters.DateFilter(field_name='hire_date', lookup_expr='gt')
hire_date__lte = django_filters.DateFilter(field_name='hire_date', lookup_expr='gt')
name__icontains = django_filters.CharFilter(field_name='name', lookup_expr='icontains')
class Meta:
model = Employee
fields = {}
views.py
def employee_view(request):
f = EmployeeFilter(request.GET, queryset=Employee.objects.all())
return render(request, 'product/employee.html', {'filter': f})
templates/my-app/employee.html
<h1>Employee</h1>
<form method="get">
{{ filter.form.as_p }}
<input type="submit"/>
</form>
<ul>
{% for employee in filter.qs %}
{{ employee.hire_date }}<br/>
{% endfor %}
</ul>
The field_name specifies the name of the field in the model ie Employee and lookup_expr is a string which specifies the filter to use on the column of table for eg gt, gte, lt, lte, exact, iexact, contains, icontains
If you want to filter against a field, but do not want it in the form, you can do the following:
import django_filters
from django import forms
class MyFilter(django_filters.FilterSet):
field = django_filters.CharFilter(
widget=forms.HiddenInput()
)
class Meta:
model = MyModel
fields = ['other_field', 'field']
The only downside is, it will show as an empty parameter in the URL, when you submit the form. But it won't affect your results, since its an empty value.

Build dropdown from database table names

I have this application where I want to deploy some web interface to a syslog server.
So the syslogserver does write his stuff into a mysql database. I already have build
some parts for the application except for this specific part where I want to build a dropdown select form, to select the hosttables inside the database.
Actually I am using flask, flask-sqlalchemy and wtforms. So I tried to implement this over the 'QuerySelectField', somehow I only get a dropdown with no table name entries shown.
I should mention that the tables inside the database itself are created on the fly. For my model I used the automap_base() Feature from sqlalchemy:
model.py
Base = automap_base()
engine = create_engine("mysql://sumuser:tehpass#127.0.0.1/syslog")
Base.prepare(engine, reflect=True)
session = Session(engine)
This is whats inside my forms:
forms.py
def factoryHelper():
return session.query("information_schema.tables.table_name from information_schema.tables where information_schema.tables.table_name like 'messages_hostname0'")
class HostSelectForm(Form):
title = TextField('Fooblah')
hostTables = QuerySelectField(query_factory=factoryHelper,allow_blank=True)
and this inside the views:
views.py
#app.route('/index/', defaults={'page':1})
#app.route('/index/page/<int:page>')
def index(page):
form = HostSelectForm()
count = session.execute("select host,facility,level,msg from messages_hostname0").rowcount
pagination = Pagination(page, PER_PAGE, count)
return render_template('index.html', pagination=pagination, form=form)
So is there anyway I can create a dropdown menu from dynamically created table names? Also if I use the automap feature? Thanks in advance.
Somehow i managed to solve this issue with this in the model.py:
def reflectTables():
for i in Base.classes.items():
yield i[0]
def stripTables():
tablelist = []
datelist = []
re0 = r"^(?P<prefix>[a-zA-Z]*)\_(?P<mid>[a-zA-Z,0-9]*)\_(?P<suffix>[0-9]*)"
myre = re.compile(re0, re.MULTILINE)
for x,table in enumerate(reflectTables()):
striptablename = re.match(myre, table.strip("\n"))
if striptablename:
tablelist.append((x, striptablename.group(2)))
datelist.append((x, striptablename.group(3)))
return dicht(tablelist,datelist)
the forms.py:
AVAILABLE_CHOICES = stripTables()
class HostSelectForm(Form):
tableSelect = SelectField('LogHost', choices=AVAILABLE_CHOICES, default=0)
and finnaly inside the views.py:
if request.method == "GET":
count = session.query("* from mytable_monitory_counts")
items = session.execute("select * from mytable_%s_%s limit %s, %s" % \
(tableslector[int(request.values['tableSelect'])][1],\
datelist[int(request.values['tableSelect'])][1], my_start_range, PER_PAGE)).fetchall()
pagination = Pagination(page=page, total=count.count(), search=search, record_name='hosts')
if not items and page != 1:
in_error()
return render_template('index.html', pagination=pagination, form=form, items=items)

Unique validator in WTForms with SQLAlchemy models

I defined some WTForms forms in an application that uses SQLALchemy to manage database operations.
For example, a form for managing Categories:
class CategoryForm(Form):
name = TextField(u'name', [validators.Required()])
And here's the corresponding SQLAlchemy model:
class Category(Base):
__tablename__= 'category'
id = Column(Integer, primary_key=True)
name = Column(Unicode(255))
def __repr__(self):
return '<Category %i>'% self.id
def __unicode__(self):
return self.name
I would like to add a unique constraint on the form validation (not on the model itself).
Reading the WTForms documentation, I found a way to do it with a simple class:
class Unique(object):
""" validator that checks field uniqueness """
def __init__(self, model, field, message=None):
self.model = model
self.field = field
if not message:
message = u'this element already exists'
self.message = message
def __call__(self, form, field):
check = self.model.query.filter(self.field == field.data).first()
if check:
raise ValidationError(self.message)
Now I can add that validator to the CategoryForm like this:
name = TextField(u'name', [validators.Required(), Unique(Category, Category.name)])
This check works great when the user tries to add a category that already exists \o/
BUT it won't work when the user tries to update an existing category (without changing the name attribute).
When you want to update an existing category : you'll instantiate the form with the category attribute to edit:
def category_update(category_id):
""" update the given category """
category = Category.query.get(category_id)
form = CategoryForm(request.form, category)
The main problem is I don't know how to access the existing category object in the validator which would let me exclude the edited object from the query.
Is there a way to do it? Thanks.
In the validation phase, you will have access to all the fields. So the trick here is to pass in the primary key into your edit form, e.g.
class CategoryEditForm(CategoryForm):
id = IntegerField(widget=HiddenInput())
Then, in the Unique validator, change the if-condition to:
check = self.model.query.filter(self.field == field.data).first()
if 'id' in form:
id = form.id.data
else:
id = None
if check and (id is None or id != check.id):
Although this is not a direct answer I am adding it because this question is flirting with being an XY Problem. WTForms primary job is to validate that the content of a form submission. While a decent case could be made that verifying that a field's uniqueness could be considered the responsibility of the form validator, a better case could be made that this is the responsibility of the storage engine.
In cases where I have be presented with this problem I have treated uniqueness as an optimistic case, allowed it to pass form submission and fail on a database constraint. I then catch the failure and add the error to the form.
The advantages are several. First it greatly simplifies your WTForms code because you do not have to write complex validation schemes. Secondly, it could improve your application's performance. This is because you do not have to dispatch a SELECT before you attempt to INSERT effectively doubling your database traffic.
The unique validator needs to use the new and the old data to compare first before checking if the data is unique.
class Unique(object):
...
def __call__(self, form, field):
if field.object_data == field.data:
return
check = DBSession.query(model).filter(field == data).first()
if check:
raise ValidationError(self.message)
Additionally, you may want to squash nulls too. Depending on if your truly unique or unique but allow nulls.
I use WTForms 1.0.5 and SQLAlchemy 0.9.1.
Declaration
from wtforms.validators import ValidationError
class Unique(object):
def __init__(self, model=None, pk="id", get_session=None, message=None,ignoreif=None):
self.pk = pk
self.model = model
self.message = message
self.get_session = get_session
self.ignoreif = ignoreif
if not self.ignoreif:
self.ignoreif = lambda field: not field.data
#property
def query(self):
self._check_for_session(self.model)
if self.get_session:
return self.get_session().query(self.model)
elif hasattr(self.model, 'query'):
return getattr(self.model, 'query')
else:
raise Exception(
'Validator requires either get_session or Flask-SQLAlchemy'
' styled query parameter'
)
def _check_for_session(self, model):
if not hasattr(model, 'query') and not self.get_session:
raise Exception('Could not obtain SQLAlchemy session.')
def __call__(self, form, field):
if self.ignoreif(field):
return True
query = self.query
query = query.filter(getattr(self.model,field.id)== form[field.id].data)
if form[self.pk].data:
query = query.filter(getattr(self.model,self.pk)!=form[self.pk].data)
obj = query.first()
if obj:
if self.message is None:
self.message = field.gettext(u'Already exists.')
raise ValidationError(self.message)
To use it
class ProductForm(Form):
id = HiddenField()
code = TextField("Code",validators=[DataRequired()],render_kw={"required": "required"})
name = TextField("Name",validators=[DataRequired()],render_kw={"required": "required"})
barcode = TextField("Barcode",
validators=[Unique(model= Product, get_session=lambda : db)],
render_kw={})
Looks like what you are looking for can easily be achieved with ModelForm which is built to handle forms that are strongly coupled with models (the category model in your case).
To use it:
...
from wtforms_components import Unique
from wtforms_alchemy import ModelForm
class CategoryForm(ModelForm):
name = TextField(u'name', [validators.Required(), Unique(Category, Category.name)])
It will verify unique values while considering the current value in the model. You can use the original Unique validator with it.
This worked for me, simple and easy:
Make sure that every time when a new row created in DB it must have unique name in colomn_name_in_db otherwise it will not work.
class SomeForm(FlaskForm):
id = IntegerField(widget=HiddenInput())
fieldname = StringField('Field name', validators=[DataRequired()])
...
def validate_fieldname(self, fieldname):
names_in_db = dict(Model.query.with_entities(Model.id,
Model.colomn_name_in_db).filter_by(some_filtes_if_needed).all())
if fieldname.data in names_in_db.values() and names_in_db[int(self.id)] != fieldname.data:
raise ValidationError('Name must be unique')