When I render the form the entire ndb model (PartModel is coming through. I'd like to have it render only the part_number property, but actual store the PartModel. I've dug through the WTForms documentation and I'm having a tough time figuring out the right way to do this.
Any thoughts? Relevant files are shown below.
new_dimension.html
<div class="control-group">
<div class="control-label">{{ form.dimension_part.label }}</div>
<div class="controls">
{{ form.dimension_part|safe }}
{% if form.dimension_part.errors %}
<ul class="errors">
{% for error in form.dimension_part.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
views.py
def list_dimensions(part_id=None):
"""List dimensions"""
if part_id is not None:
print "************Part ID is",part_id
Part = PartModel.get_by_id(part_id)
dimensions = DimensionModel.query(DimensionModel.dimension_part==Part.key).fetch()
title = "Dimensions for {}".format(Part.part_name)
else:
dimensions = DimensionModel.query() #creates Query object
title = "All Dimensions"
form = DimensionForm() #pulls in DimensionForm
if form.validate_on_submit():
dimension = DimensionModel(
dimension_part=form.dimension_part.data.key,
dimension_name=form.dimension_name.data,
dimension_value=form.dimension_value.data,
dimension_usl = form.dimension_usl.data,
dimension_lsl = form.dimension_lsl.data,
added_by=users.get_current_user()
)
try:
dimension.put()
dimension_id = dimension.key.id()
flash(u'Dimension %s successfully saved.' % dimension_id, 'success')
return redirect(url_for('list_dimensions'))
except CapabilityDisabledError:
flash(u'App Engine Datastore is currently in read-only mode.', 'info')
return redirect(url_for('list_dimensions'))
return render_template('list_dimensions.html', dimensions=dimensions, form=form,title=title) #pushes query object into template
forms.py
PartForm = model_form(PartModel, wtf.Form, field_args={
'part_name': dict(validators=[validators.Required()]),
'part_number': dict(validators=[validators.Required()])
})
models.py
class PartModel(ndb.Model):
"""Part"""
part_name = ndb.StringProperty(required=True)
part_number = ndb.FloatProperty(required=True)
added_by = ndb.UserProperty()
timestamp = ndb.DateTimeProperty(auto_now_add=True)
class DimensionModel(ndb.Model):
"""Dimension"""
dimension_part = ndb.KeyProperty(required=True,kind=PartModel)
dimension_name = ndb.StringProperty(required=True)
dimension_value = ndb.FloatProperty(required=True)
dimension_usl = ndb.FloatProperty(required=True)
dimension_lsl = ndb.FloatProperty(required=True)
added_by = ndb.UserProperty()
timestamp = ndb.DateTimeProperty(auto_now_add=True)
Related
I'm trying to add functionality to add a IntegerField next to every 'stockItem' in the template so that the user can type how many of that item was needed and then update the 'count' value in the 'Stock' model. As for now it only works when the user only selects one item. (I know that how I implement this now would never work but I try to show how I intend it to work)
Models:
class Machine_Service(models.Model):
title = models.CharField(max_length = 100)
stockItem = models.ManyToManyField(Stock)
date = models.DateTimeField(default=timezone.now)
comment = models.TextField()
machine = models.ForeignKey(Machine, on_delete=models.CASCADE)
def __str__(self):
return self.title
class Stock(models.Model):
title = models.CharField(max_length=100)
count = models.IntegerField(default=10)
minLimit = models.IntegerField(default=1)
resellerCompany = models.CharField(max_length=100)
resellerPerson = models.CharField(max_length=100)
resellerEmail = models.EmailField()
def __str__(self):
return self.title
(I left the 'Machine' model out of this because it does not belong to the question)
view:
def CreateService(request):
if request.method == 'POST':
form = CreateServiceForm(request.POST)
if form.is_valid():
m = Machine.objects.get(id=form['machine'].value())
service = Machine_Service(title=form['title'].value(), date=form['date'].value(), comment=form['comment'].value(), machine=m)
service.save()
items = form['stockItem'].value()
for item in items:
s = Stock.objects.get(id=item)
service.stockItem.add(s)
service.save()
return redirect('Machines-Home')
else:
form = CreateServiceForm()
context = {
'form': form
}
form:
class CreateServiceForm(forms.ModelForm):
count = forms.IntegerField(required=False)
class Meta:
model = Machine_Service
fields = ['title', 'stockItem', 'count', 'date', 'comment', 'machine']
template:
(I do not need to use crispy)
{% extends "maskinNytt/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">New Post!</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post</button>
</div>
</form>
</div>
{% endblock content %}
In your case your models.py should look something like this:
class Machine_Service(models.Model):
stockItem = models.ManyToManyField(Stock, through='NeededItems')
...
class Stock(models.Model):
...
class NeededItem(models.Model):
machine_service = models.ForeignKey(Machine_Service, on_delete=models.CASCADE)
stock = models.ForeignKey(Stock, on_delete=models.CASCADE)
amount = models.IntegerField()
To specify this amount and use it in your views/forms you need to just create instances of NeededItem.
I assume you have a form for the MachineService and one for a NeededItem. For the NeededItems you need a FormSet, views.py:
from django import formset_factory
NeededItemFormset = formset_factory(NeededItemForm, extra=2)
def view(request):
if request.method == 'POST':
machine_form = MachineForm(request.POST, prefix='machine')
needed_item_formset = NeededItemFormset(request.POST, prefix='items')
if machine_form.is_valid() and needed_item_formset.is_valid():
machine_form.save()
needed_item_formset.save()
return redirect('blabla')
else:
machine_form = MachineForm()
needed_item_formset = NeededItemFormset()
return render('template', {'m_form': machine_form, 'n_form': needed_item_formset})
The template then might look something like this.
<form>
{% machine_service_form.as_p %}
{% needed_item_formset.as_p %}
</form>
(I didn't test this, so I don't give guarantees it works, but I think you should have enough pointers to figure out which things you need).
When one would make a Machine_Service they would also need a form (or multiple) for the NeededItem model. That means you can basically ignore the Stock model when creating Machine_Services.
I have a Django project with a database of Song Objects that users can search through.
My models.py looks like this:
class Songs(models.Model):
title = models.CharField(max_length=100)
artist = models.CharField(max_length=100)
link = models.CharField(max_length=255, unique=True)
album = models.CharField(max_length=100)
duration = models.CharField(max_length=40) # Duration in num_secs
and my views.py looks like this:
class ResultsView(ListView):
template_name = os.path.join(APPNAME, "results.html")
model = Songs
context_object_name = 'results'
paginate_by = 60
ordering = ['title']
def get_context_data(self, **kwargs):
context = super(ResultsView, self).get_context_data(**kwargs)
context['query'] = self.request.GET.get('query')
return context
def get_queryset(self, **kwargs):
query = self.request.GET.get('query')
query_set = Songs.objects.all()
results = query_set.filter(title__icontains=query)
return list(results)
And my results.html template looks like this:
{% if results %}
<div class="container-fluid">
<div class="row">
{% for result in results %}
<div class="col-md-2 result-col">
<a data-toggle="tooltip" title="{{ result.title }}" target="_blank" href="/song/{{ result.id }}">
<div class="result-text">{{ result.title }} </div>
<div class="result-dur">{{ result.duration }}</div>
</a>
</div>
{% endfor %}
</div>
</div>
{% else %}
<h2>No Results</h2>
{% endif %}
Due to the way the data is initially stored in my DB, the duration of each song is stored as the number of seconds, ie a song that is 2 minutes long is stored with a duration of 120. However, I want to display it on the template in the format: "hh:mm:ss".
How can I modify my ResultsView class so that I can parse the duration field of all of the objects in my query_set so that they have a more readable duration?
Note: to be 100% clear, I know how to do the actual conversion, using strftime. What I don't know, is how to access the Song.duration fields in my ResultsView.
You could add a method or property to your model:
#property
def converted_time(self):
return <your converted time>
And in your template {{ result.converted_time }}
You can just use a custom template tag.
Ref
snippet
#register.filter()
def formatSeconds(s):
mins = math.floor(s / 60);
secs = math.floor(s - (mins * 60));
return "%d:%02d" % (mins, secs);
I just started learning Django and for this project I'm following the "Tango with Django" tutorial book. I have a problem with the input field of a form not showing up, while the button seems to be rendered fine.
Here's my code:
models.py
[...]
class Idea(models.Model):
keyword = models.ForeignKey(Keyword)
word = models.CharField(max_length=120)
count = models.IntegerField(default=1)
def __str__(self):
return self.word
forms.py
[...]
class Meta:
model = Keyword
fields = ('name',)
class IdeaForm(forms.ModelForm):
word = forms.CharField(max_length=120)
count = forms.IntegerField(widget=forms.HiddenInput(), initial=1)
class Meta:
model = Idea
fields = ('word',)
exclude = ('keyword',)
views.py
[...]
def keyword_detail(request, keyword_name_slug):
form = IdeaForm()
context_dict = {}
try:
keyword = Keyword.objects.get(slug=keyword_name_slug)
ideas = Idea.objects.filter(keyword=keyword)
context_dict['keyword'] = keyword
context_dict['ideas'] = ideas
except Keyword.DoesNotExist:
context_dict['keyword'] = None
context_dict['ideas'] = None
if request.method == 'POST':
form = IdeaForm(request.POST)
if form.is_valid():
idea = form.save(commit=False)
idea.keyword = keyword
idea.count = 1
idea.save()
return keyword_detail(request, keyword_name_slug)
else:
print(form.errors)
context_dict['form'] = form
return render(request, 'openminds/keyword.html', context_dict)
keyword.html
[...]
<h3>Add a new Idea</h3>
<div>
<form id="idea_form" method="post" action="">{% csrf_token %}
{% for hidden in forms.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in forms.visible_fields %}
{{ field.errors }}
{{ field }}
{% endfor %}
<input type="submit" name="submit" value="Add Idea" />
</form>
</div>
I think you're passing in form to the template, but attempting to use forms.
EDIT: Added model classes at the bottom.
I have this view code that takes input from a Django form and uses it to search the database for values.
class SearchResults(generic.FormView):
template_name = 'search_results.html'
context_object_name = 'submissions'
form_class = SearchForm
model = Submissions
def get_context_data(self, **kwargs):
context = super(SearchResults, self).get_context_data(**kwargs)
level = self.request.GET['level']
media_type = self.request.GET['media_type']
tags = MajorTags.objects.filter(
tag__icontains=self.request.GET['search'])
tag_ids = [t.id for t in tags]
context['results'] = Submissions.objects.filter(
tags__id__in=tag_ids,
level__contains=level,
media_type__contains=media_type)
return context
That then ends up being rendered by this template.
{% extends "base.html" %}
{% block content %}
<form action="{% url 'search_results' %}" method="get" class="global-search">
{% csrf_token %}
{{ form }}.
<input type="submit" value="Go" />
</form>
<ul>
{% for resource in results %}
<li> {{resource.url}} {{resource.stats_set}}
</li>
{% endfor %}
</ul>
{% endblock %}
The problem is that {{resource.stats_set}} returns nothing in the template. (The exact output is doc_sub.Stats.None.) However, when I run the following code in the shell, I get the result I'm looking for.
from doc_sub.models import MajorTags, Submissions
tags = MajorTags.objects.filter(tag__icontains='Theology')
tag_ids = [t.id for t in tags]
level = 'Introductory'
media_type='HTML'
context = Submissions.objects.filter(tags__id__in=tag_ids, level__contains=level, media_type__contains=media_type)
for i in context:
print i.stats_set.values()
Here are the necessary models:
class Submissions(models.Model):
LEVEL = (
('Introductory', 'Introductory'),
('Intermediate', 'Intermediate'),
('Academic', 'Academic'),
)
MEDIA_TYPE = (
('HTML', 'HTML'),
('PDF', 'PDF'),
('Video', 'Video'),
('Other', 'Other'),
)
id = models.AutoField(primary_key=True)
url = models.URLField(unique=True)
tags = models.ForeignKey(MajorTags, default=0)
level = models.CharField(choices=LEVEL, max_length=25)
media_type = models.CharField(choices=MEDIA_TYPE, max_length=25)
def __unicode__(self):
return self.url
class Stats(models.Model):
url = models.ForeignKey(Submissions)
id = models.AutoField(primary_key=True)
avg = models.FloatField(default=0)
std_dev = models.FloatField(default=0)
def __unicode__(self):
return self.url.url + " " + str(self.avg)
Well, I figured it out. If I had been smart and RTFM I may have figured it out sooner, however, I was trying to access the value the wrong way. I was doing this: print t.stats_set.values()[0]['avg'] like I was accessing values in a normal python context, but I needed to be doing this: resource.stats_set.values.0.avg
Im new in Django, and Im trying to create a form for add books for my app. But I want the date of publication not included in the form. Instead I want the current system date is obtained and will " add" the form to save my model . How could I do this?
There is part of my views.py:
def add_book(request):
if request.method == 'POST':
form = BookForm(request.POST)
if form.is_valid():
new_book = form.save(commit=False)
new_book.publication_date = django_timezone
new_book.save()
return HttpResponseRedirect('/thanks/')
else:
print form.errors
else:
form = BookForm()
return render_to_response('add_book.html',{'form':form})
There is my forms.py:
class BookForm(ModelForm):
class Meta:
model = Book
exclude = ('publication_date',)
And my model Book:
class Book(models.Model):
title = models.CharField(max_length = 100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
num_pages = models.IntegerField(blank = True, null = True)
class Admin:
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publisher', 'publication_date')
ordering = ('-publication_date',)
search_fields = ('title',)
def __str__(self):
return self.title
I used this template for form:
{% extends 'base.html' %}
{% block title %}Add a new Book{% endblock %}
{% block content %}
<h3> Here you can add a new book into the local DataBase </h3>
<form action="." method="post">{% csrf_token %}>
<div class="fieldWrapper">
{{ form.title.errors }}
<label for="id_title">Book Title</label>
{{ form.title }}
</div>
<div class="fieldWrapper">
{{ form.authors.errors }}
<label for="id_authors">Authores</label>
{{ form.authors }}
</div>
<div class="fieldWrapper">
{{ form.publisher.errors }}
<label for="id_publisher">Publishers</label>
{{ form.publisher }}
</div>
<div class="fieldWrapper">
{{ form.num_pages.errors }}
<label for="id_num_pages">Number of Pages</label>
{{ form.num_pages }}
</div>
<p><input type="submit" value="Submit"></p>
</form>
{% endblock %}
I've temporarily disabled Django csrf because I do not need for my purpose
To do that, you need to pass the default argument as timezone.now in publication_date model field.
models.py
class Book(models.Model):
title = models.CharField(max_length = 100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
# pass the default argument in publication_date field
publication_date = models.DateField(default=timezone.now)
num_pages = models.IntegerField(blank = True, null = True)
class Admin:
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publisher', 'publication_date')
ordering = ('-publication_date',)
search_fields = ('title',)
def __str__(self):
return self.title
views.py
After doing that, you can directly call .save() on modelform. Now, the book object will be created with the aware datetime.
def add_book(request):
if request.method == 'POST':
form = BookForm(request.POST)
if form.is_valid():
new_book = form.save() # directly save the book object
return HttpResponseRedirect('/thanks/')
else:
print form.errors
else:
form = BookForm()
return render_to_response('add_book.html',{'form':form})
Why we did not use auto_now_add argument in the model field?
From django's auto_now_add documentation:
Django's auto_now_add uses current time which is not timezone aware. So, it is better to explicitly specify default value using default argument.
If you want to be able to modify this field, set default=timezone.now
(from django.utils.timezone.now()) instead of auto_now_add=True.