I asked this question earlier, but now I'm having trouble sorting out how to use drop downs (or even better, autofill fields) for one of the forms of a multi-form view.
The models in play are Book, BookDetails, and Genre. BookDetails is a linking table to Genre (and other similar tables) so that I can have a static list of genres etc. with unique IDs and foreign keys to BookDetails.
Right now I have this:
#views.py
def BookFormView(request):
genre = Genre.objects.all()
if request.method == "POST":
book_form = BookForm(request.POST, prefix='book')
bookdetails_form = BookDetailsForm(request.POST, prefix='bookdetails')
selected_genre = get_object_or_404(Genre, pk=request.POST.get('genre_id'))
genre.id = selected_genre
genre.save()
if book_form.is_valid() and bookdetails_form.is_valid():
book_form.save()
bookdetails_form.save()
return HttpResponseRedirect("/books/")
else:
book_form = bookForm(prefix='book')
bookdetails_form = BookDetailsForm(prefix='bookdetails)
return render(request, 'books/createbook.html',
{'book_form' : book_form,
'bookdetails_form': bookdetails_form,
'genre':genre,})
#createbook.html
<select name="genre", id="genre" form="bookform">
{% for entry in genre %}
<option value="{{ entry.id }}">
{{ entry.name }}
</option>
{% endfor %}
</select>
The form displays properly on the page, dropdown menu with options from the database included. However, when I hit submit to store the information to the database I get an error saying No Genre matches the given query The other posts on SO that regard this error don't seem to be from the same context. I think that it might be something to do with selecting a name but storing an id (for the genres), but otherwise I'm at a loss.
Normally, the way you'd do this with a form in django is not by manually pulling something out of the POST dict, but by using a ModelChoiceField:
https://docs.djangoproject.com/en/1.8/ref/forms/fields/#modelchoicefield
Was there a specific reason you didn't do that?
Also, it appears you're using the genre variable incorrectly for two different things. You initialize it with a queryset, but then try to treat it like a Genre instance later in the code. That's going to cause problems not to mention the fact that I don't think your genre.id = ... line is going to do what you expect it to.
Also, it's against style conventions to use title-casing for function names. If you're going to be doing much coding in Python, it's probably worth taking a look at the officially accepted PEP8 style guide here:
https://www.python.org/dev/peps/pep-0008/
There are a few other problems in the code but I'm not sure it's worth calling them out.
Related
There's a model represents BBS.
I want to make change the value without update database. because I want to preserve the database value. To be precise at display time, I want it to be displayed as '[collabo]' + article.title this is what I am doing at the moment.
below is combine '[collabo]' and all of title with for loop
for article in articles:
article.title = '[collabo]'+article.title
is there any way to change the title value at one line of code? I don't want to change or update database. Or is there a better way.
If you want to do this in a single database query it's one line longer than what you have now!! but it's far more efficient.
from django.db.models import Value
from django.db.models.functions import Concat
Article.objects.annotate(new_title = Concat(V('[collabo]'),'title')))
The annotate method in the queryset is your friend here (with a little help from Concat and Value)
You could also do this at the template level
articles = Article.objects.all()
render('template.html',{'articles': articles})
And then
{% for article in articles %}
[collabo] {{ article.title }}
{% endfor %}
you may work with methods of the model class to give specific modification you want.
models.py
class Article(models.Model):
# some fields ...
def edited_title(self):
return '[collabo] {}'.format(self.title)
then you can exploit it in the templates with {{article.edited_title}}.
I am brand new to web development, Django, python, html, etc. I have a basic Django app that displays a list of publication titles that have been entered into the database. This works fine.
I now want to make it so that each publication title is a link that - when clicked on - renders another template with the details of the publication that was clicked. So far, I know how to get the publication link to render a template, but I am trying to figure out how to pass in the publication title to the hyperlink so that the data that is rendered in the details template will be specific to the title that was chosen.
Here is what I have in my publication template which displays all the publications (it is incorrect, but hopefully it clarifies what I am trying to do):
<html>
<head><title>Publications</title></head>
<body>
<h1>Publications</h1>
<ul>
{% for publication in publication_list %}
<li><strong>{{ publication.title}}</strong></li>
{% endfor %}
</ul>
</body>
</html>
For the sake of context, the url pattern that handles this is:
url(r'^(?P<detail_type>\w+)/(?P<link_item>\w+)/detail$', get_details)
And the view function is:
// note: I may have some of the logic/syntax wrong here, but this is the basic idea
def get_details(request, detail_type=None, link_item=None):
if detail_type == "publications":
publication = Publication.objects.filter(title__iexact=link_item)
return render(request, 'publication_detail.html', {'detail_type' : detail_type, 'publication' : publication})
elif ....
Like I said, I am very much a beginner so if I am approaching this in wrong way, any suggestions or resources are appreciated. Thanks.
If you use named url patterns you can easily do this with the url template tag.
urls.py
url(r'^(?P<detail_type>\w+)/(?P<link_item>\w+)/detail$', get_details, name='details')
template
{% url 'details' 'publications' publication.title %}
I hope you know about SlugField too, it is much better for urls than a normal CharField.
An alternative:
urls.py
url(r'^(?P<detail_type>\w+)/(?P<pk>\w+)/detail$', get_details, name='details')
template
{% url 'details' 'publications' publication.pk %}
views.py
def get_details(request, detail_type=None, pk=None):
if detail_type == "publications":
publication = Publication.objects.get(pk=pk)
return render(request, 'publication_detail.html', {'detail_type' : detail_type, 'publication' : publication})
elif ....
This uses the primary key of the entry instead of the title. If you want to have a url with the title in it you will want to add and use a SlugField on your model.
This looks pretty good to me, although you may want to use get as opposed to filter in your view function if all the publication titles are unique and you want to pass an instance of Publication rather than a queryset (containing one item) into the detail template. This would throw an error of there were 0 or >1 matches, but it sounds like that's probably the behavior you'd want
However, I'm not sure what it is that you're missing here. What does publication_detail.html look like? You should have essentially everything you need in the above code to render the details, assuming they're all contained in the relevant Publication instance.
I would like to display all results which match selected facets even though a search query has not been inserted. Similar to how some shop applications work e.g. Amazon
e.g. Show all products which are "blue" and between $10-$100.
Haystack does not return any values if a search query is not specified.
Any ideas how I can get around it?
Thanks!
If anyone is still looking, there's a simple solution suggested in haystack code:
https://github.com/toastdriven/django-haystack/blob/master/haystack/forms.py#L34
class SearchForm(forms.Form):
def no_query_found(self):
"""
Determines the behavior when no query was found.
By default, no results are returned (``EmptySearchQuerySet``).
Should you want to show all results, override this method in your
own ``SearchForm`` subclass and do ``return self.searchqueryset.all()``.
"""
return EmptySearchQuerySet()
Why No Results?
I imagine you're using a search template similar to the one in the haystack getting started documentation. This view doesn't display anything if there is no query:
{% if query %}
{# Display the results #}
{% else %}
{# Show some example queries to run, maybe query syntax, something else? #}
{% endif %}
The second problem is that the default search form's search() method doesn't actually search for anything unless there's a query.
Getting Results
To get around this, I'm using a custom search form. Here's an abbreviated sample:
class CustomSearchForm(SearchForm):
...
def search(self):
# First, store the SearchQuerySet received from other processing.
sqs = super(CustomSearchForm, self).search()
if not self.is_valid():
return sqs
filts = []
# Check to see if a start_date was chosen.
if self.cleaned_data['start_date']:
filts.append(SQ(created_date__gte=self.cleaned_data['start_date']))
# Check to see if an end_date was chosen.
if self.cleaned_data['end_date']:
filts.append(SQ(created_date__lte=self.cleaned_data['end_date']))
# Etc., for any other things you add
# If we started without a query, we'd have no search
# results (which is fine, normally). However, if we
# had no query but we DID have other parameters, then
# we'd like to filter starting from everything rather
# than nothing (i.e., q='' and tags='bear' should
# return everything with a tag 'bear'.)
if len(filts) > 0 and not self.cleaned_data['q']:
sqs = SearchQuerySet().order_by('-created_date')
# Apply the filters
for filt in filts:
sqs = sqs.filter(filt)
return sqs
Also, don't forget to change the view:
{% if query or page.object_list %}
{# Display the results #}
{% else %}
{# Show some example queries to run, maybe query syntax, something else? #}
{% endif %}
Actually, the view code is a little hackish. It doesn't distinguish query-less searches with no results from search with no parameters.
Cheers!
Look at SearchQuerySet.
This should be possible if color and price has been defined in your SearchIndex:
sqs = SearchQuerySet().filter(color="blue", price__range=(10,100))
You can limit the query to certain models by adding models(Model) to the SearchQuerySet. So if you want to limit your query to the model Item use:
sqs = SearchQuerySet().filter(color="blue", price__range=(10,100)).models(Item)
Following form display all the result if not query string is present. Now you can add custom filters.
from your_app.forms import NonEmptySearchForm
url(r'^your_url$',
SearchView(template='search.html',searchqueryset=sqs,form_class=NonEmptySearchForm), name='haystack_search'),
forms.py
#Overridding because the default sqs is always none if no query string is present
class NonEmptySearchForm(SearchForm):
def search(self):
if not self.is_valid():
return self.no_query_found()
sqs = self.searchqueryset.auto_query(self.cleaned_data['q'])
if self.load_all:
sqs = sqs.load_all()
return sqs
Stumpy Joe Pete's answer is pretty spot on, but as he mentioned, the template if query or page.object_list check is a little hacked. A better way of solving this would be to create your own SearchForm which would still find something if q is empty - will not repost that - AND to customize the SearchView with something like:
class MySearchView(SearchView):
def get_query(self):
query = []
if self.form.is_valid():
for field in self.form:
if field.name in self.form.cleaned_data and self.form.cleaned_data[field.name]:
query.append(field.name+'='+str(self.form.cleaned_data[field.name]))
return ' AND '.join(query)
In most cases, you won't even be using the query value, so you could just as well do a quick check if any of the fields is set and return True or something like that.. or of course you can modify the output any way you want (I'm not even 100% sure my solution would work for all field types, but you get the idea).
I have decided to use Django-Simple-History for building a history of my models. In turn using that to build the dashboard. I have run into a bit of a snag though. I'm trying to output [User] [added,changed, deleted] [object] on/at [time] but I can't figure it out for the life of me.
So far I am able to display the historical record on the template but I can't access anything else, am I missing something?
I was hoping someone with knowledge of Simple History can help, since I couldn't get a hold of the author.
Here is the code snippets I have so far.
Models.py
from simple_history.models import HistoricalRecords
class Project(django.db.models.Model):
...
history = HistoricalRecords()
Views.py
#login_required
def addTMProject(request):
user = request.user
if request.method == 'POST':
form = TimeMaterialsForm(request.POST)
if form.is_valid():
project = form.save(commit=False)
project.created_by = request.user
today = datetime.date.today()
project.pre_quote = "%s-" % (str(today.year)[2:4])
project.quote = Project.objects.latest().quote+1
project.save()
project.history.all()
...
And I have also passed it on my dashboard/views.py so I have access to it.
#login_required
def view_dash(request):
today = datetime.date.today()
user = request.user
proj_perm = user.has_perm('project.add_project')
project = Project.objects.all().order_by('-proj_name')
query = Project.objects.all().order_by('-id')[:5]
que_quotes = Project.objects.filter(status__value__exact = 'Quote')
expired = FollowUp.objects.filter(next_followup__lte=today).order_by('next_followup').filter(archived=False)
log = LogEntry.objects.select_related().all().order_by("-id")
hist = Project.history.all()
return render_to_response('dashboard/home.html', {'user': user, 'project': project, 'query':query, 'que_quotes':que_quotes, 'expired':expired,
'proj_perm':proj_perm, 'log': log, 'hist':hist,}, context_instance=RequestContext(request))
And finally a snippet from my template. As it is right now, the {{ h }} shows "Testing Simple Records as of 2011-04-29 10:43:57" on the template
home.html
{% if user.is_authenticated %}
<div id="large_box">
<h5>Activity</h5>
{% for h in hist %}
<ul>
<li>{{ h }}</li>
</ul>
{% endfor %}
If anyone could help or point me to some more in depth documentation, then that would be great!
Thanks everyone!
Django-Simple-History simply creates a model (and associated database table) that mirrors the object you tie it to and adds four additional fields: history_id, history_date, history_type, and history_object.
history_id: standard primary key
history_date: datetime for when the change occurred
history_type: one of +, ~, -. (+ means added, ~ means changed, and - means deleted)
history_object: representation of the model that history is being stored for
So, at most basic level, you can roughly get "[added,changed, deleted] [object] on/at [time]" in your output, using something to the effect of:
{{ h.history_type }} {{ h.history_object }} on/at {{ h.history_date }}
You'll probably want to create template tag or something to convert the +, ~, and - to the more understandable, 'Created', 'Changed', 'Deleted'. {{ h.history_object }} should return the object's __unicode__, I'm assuming, so you might need to make some modifications there or return something like {{ h.history_object.__class__ }} or {{ h.history_object._meta.verbose_name }}, instead. (Not sure if those will actually work in practice though.) And, of course, you can apply the date filter to {{ h.history_date }} to make it any format you want.
Getting the user is more difficult. Django-Simple-History doesn't seem to store this data, so there's no record of what user made the modification. However, since it basically duplicates the object as it existed, you could probably get away with adding a modified_by field to your model and filling that with request.user pre-save. Then, when Django-Simple-History does its thing, that field would be copied over like the rest and be available via {{ h.modified_by }}.
I'm assuming that the only problem you're having is with the displaying of the historical data and not the actual saving portion of it.
I'm not sure what fields you have in your Project model, but it looks like the history field is treated like a foreign key field. This foreign key's table contains the same fields that your Project model does. So, if you want to access the fields you'd have to do something like this in your template:
...
{% for h in hist %}
<ul>
<li>{{h.field1}} {{h.field2}} {{h.field3}} on {{h.field4}}</li>
</ul>
{% endfor %}
...
I found this page (http://qr7.com/2010/10/django-simple-history-ftw/) which was quite helpful, but you'll have to play around with the history field names. I wasn't quite sure what they actually were.
Hope that helps.
I'm working on my first Django application. In short, what it needs to do is to display a list of film titles, and allow users to give a rating (out of 10) to each film. I've been able to use the {{ form }} and {{ formset }} syntax in a template to produce a form which lets you rate one film at a time, which corresponds to one row in a MySQL table, but how do I produce a form that iterates over all the movie titles in the database and produces a form that lets you rate lots of them at once?
At first, I thought this was what formsets were for, but I can't see any way to automatically iterate over the contents of a database table to produce items to go in the form, if you see what I mean.
Currently, my views.py has this code:
def survey(request):
ScoreFormSet = formset_factory(ScoreForm)
if request.method == 'POST':
formset = ScoreFormSet(request.POST, request.FILES)
if formset.is_valid():
return HttpResponseRedirect('/')
else:
formset = ScoreFormSet()
return render_to_response('cf/survey.html', {
'formset':formset,
})
And my survey.html has this:
<form action="/survey/" method="POST">
<table>
{{ formset }}
</table>
<input type = "submit" value = "Submit">
</form>
Oh, and the definition of ScoreForm and Score from models.py are:
class Score(models.Model):
movie = models.ForeignKey(Movie)
score = models.IntegerField()
user = models.ForeignKey(User)
class ScoreForm(ModelForm):
class Meta:
model = Score
So, in case the above is not clear, what I'm aiming to produce is a form which has one row per movie, and each row shows a title, and has a box to allow the user to enter their score.
If anyone can point me at the right sort of approach to this, I'd be most grateful.
"At first, I thought this was what formsets were for, but I can't see any way to automatically iterate over the contents of a database table to produce items to go in the form, if you see what I mean."
You need to get a queryset. And you need to provide that queryset to your form as initial data. See using initial data with a formset for the code.
initial = [ list of { dictionaries }, one per form ]
Interestingly, this is a direct feature of the model API through the values method of a queryset.
I have found my answer, using modelformset_factory instead formset_factory solves the problem, Thanks...