Around one year ago I started to write my first application in PyQt5, where there are many fields:
I know it's not good-looking, but the main point was to learn PyQt5.
Now I want to make it useable on mobile. Since there are many people after IT studies knowing Java, I think I have a better chance to get the first job knowing Django + Javascript/jQuery. Here is the question:
Each "F1, F2, F3" is a separate widget. The screen on a phone is small, so I think about displaying them one by one. Easiest approach is probably this way (.html), but maybe there is a better one:
{% for field in GoldArrays %}
<p>{{field.text}} <input type="number" name="textfield"> {{field.number}}</p>
{% endfor %}
It's for F3/F4 widget. 'text' will return 200zl, 100zl, etc, number is just a DecimalField.
The thing is I'm very fresh to web development and Django. As I understand I should create a separate template for each widget. I want to collect data from a widget after pressing "next" button and then store it to SQLite database, so the progress will be saved. Should I put sql queries in views.py? What should be in forms.py and models.py? An example of two user inputs is highly appreaciated.
Here is the next question... There is a decent chance, that I'll want to set some rules for many fields, like highlighting value so the user doesn't need to delete the value, or to automatically set 0 in case there is None as input. I know I can do that with JS later, but can I do it with Django? I hope to write a code, that is easy to maintain.
Help please :)
Using views (controllers in Django) you can add context data.
It has next structure:
{% for item in form_set %}
{{ item }}
{% endfor %}
Jinja2 is templating language, so all vars from backend you must write in brackets
{{ each_var_from_django }}
View will be next:
class CreateOrderView(LoginRequiredMixin, CreateView):
template_name = 'dashboard/index.html'
success_url = reverse_lazy('namespace:url')
def get_context_data(self, *args, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['form_set'] = Model.objects.filter()
return ctx
Related
I have a generic ListView which displays all objects from a model and I would like users to be able to choose one object for further processing by storing in session or in another model. What would be the best way to go about this?
views.py
class TranscriptListView(generic.ListView):
model = Transcript
template_name = 'transcript_list.html'
template
{% block content %}
<ul>
{% for transcript in transcript_list %}
<li>
{{transcript.name}}
<p>{{transcript.text}}</p>
</li>
{% endfor %}
</ul>
For selecting something to store in a session, I'd just do a
class SelectTranscriptView(SingleObjectMixin, View):
model = Transcript
def post(self, request, *args, **kwargs):
object = self.get_object()
request.session['selected_transcript'] = object.id
return redirect(...)
This view only accepts POST, since GET requests should be idempotent, i.e. they should not modify any state. Setting a session value is definitely modifying state. This means you'll need to use a form (or a JavaScript function) to POST data to the URL you hook this up to.
More importantly, though: Setting something in the session is not necessarily a good idea at all. Consider an advanced user deciding to open multiple tabs to your site, and trying to select one transcript for one tab, and another in the other tab. This won't be possible if you store the selected thing in the session! Instead, I'd design things so the ID of the object being edited or viewed is always in the URL (as happens with DetailViews and the ilk anyway).
I am pulling my hair out trying to add a "like" button in my site´s post app, but as i want to add it in a ListView that contains the rest of the posts entries and everyone has the option to be commented I have added a Formixin to do so, so, now i cannot add another form for the like button as it would mean two posts requests....so I am not finding a clear solution... I have read here and there about using AJAX or Json techs but as im new programing im kind of stuck in it... has anyone any tip to offer?
While using AJAX (javascript XHR requests) would be the proper way so the page doesn't need to be refreshed when just clicking a like button, you can do it without AJAX.
HTML
On the HTML side of things, you can have multiple forms (<form>), one for each post, which have a hidden input field that's the post's id. You have set that explicitly in the HTML template, e.g.
{% for post in post_list %}
<h3>{{ post.title }}</h3>
<p>{{ post.summary }}</p>
<form method="post">
{% csrf_token %}
<input type="hidden" value="{{ post.id }}" name="{{ form.id.html_name }}">
<input type="submit">Like</input>
</form>
{% endfor %}
So basically you're reusing the form multiple times, changing the "value" attribute to match the post.
Django Form
Adding the FormMixin to your view is the right step, just use the form_class to a custom LikeForm with just one field that's an IntegerField called id.
View
By adding the FormMixin you get the form_valid() method, which you'll want to override to save the like:
def form_valid(self, form):
id = form.cleaned_data['id']
try:
post = Post.objects.get(id=id)
except Post.DoesNotExist:
raise Http404
post.likes.add(self.request.user) # assuming likes is a m2m relation to user
return redirect('post_list') # this list view
Hopefully I am not so late, I had similar challenges trying to implement the same functionalities on my website.
I came to realize that each button id should be unique (Preferably the post id if blog), but the classes can be the same.
I was able to solve it. Here is an article I wrote on medium recently on the steps I followed to so get this working you can check it out here
So since i've been trying to learn django there is one thing that confuses me more than anything else, and that is getting something that works in the django shell into a format that works in models.py of views.py. So here is an example from the Documentation:
>>> Entry.objects.all().filter(pub_date__year=2006)
So I can work with the shell, there are loads of examples everywhere, what never seems to be covered is how you then put this in your code if you want to filter for other years for example. Can someone explain this or point me at documentation that explains this, as I've not found it in the django docs.
There are several ways to do that, but for the beginning and learning purposes your view should accept a year argument:
def my_view(request, year):
entries = Entry.objects.filter(pub_date__year=year)
context = {
"entries": entries
}
return TemplateResponse(request, 'my_template.html', context)
The line you are talking about is used to query the database and then filter things. This line is mostly used in views or serializers since you want the entries to be passed on to either template or as JSON to API response.
Using it in views:
def view(request):
entries = Entry.objects.filter(pub_date__year=year)
return render(request, 'index.html', {'entries': entries})
Here the object {'entries': entries} in above code is context object.
And then after that, you can use the same thing in Django Templates for example here in index.html
{% for entry in entries %}
<li> {{ entry.id }} </li>
{% endfor %}
In my Django app, the user is presented a simple form, on validation the view searches the data (some web scraping) and returns a template in the context of which we added a list of the results, we present them to the user and he/she chooses one and (this is my problem) I want to get that choice back to another view (now we want to add the selected object to the database).
I identified my problem, but I really think I'm missing something and not using Django the right way. For now this is how I display the data and put it back in a form: (jade template. Of course I do this with many more attributes so the template is quite big and I have to add an hidden input for each value I want back)
for b in result_list
h4
for aut in b.authors
div {{ aut }}
form(action='/add/', method='post') {% csrf_token %}
{% csrf_token %}
input(type='hidden', value="{{ b.authors }}", name='authors')
edit2: there is one button per book. I am just trying to return one book.
It works fine when authors has a single element, but with more than once the template doesn't return a valid json. I could give you the structure but I tend to think this is not the point.
What am I missing ? How to display search results and get the selected one to another view ?
Do I miss something about forms ? Shall I use a session variable, a context processor with middleware ? I guess the hidden input is wrong: how can I do differently ?
many thanks
edit: below I show where my problem is. However, maybe I shouldn't fix that problem but use Django the right way instead.
1- my 1st view renders a template and feeds it with the search results:
return render(request, "search/search_result.jade", {
"return_list": book_list,
})
book_list is a list of dicts where authors is a list of unicode str:
retlist[0]["authors"]
>>> [u'Ayroles, Alain']
2- my template is above. It displays a form and a hidden input. The goal is to send back a book: a dictionnary with the title, the editor… and a list of authors.
3- now the users clicks "Add this book", we are in the "add" view. Let's inspect the authors:
req = request.POST.copy()
authors = req["authors"]
>>> u"[u'Ayroles']" # note the u after the [
# this is not a valid json, I can't get a list out of it
import json
json.loads(req["authors"])
>>> *** ValueError: No JSON object could be decoded
So I passed a list from the first view to the template, I parsed it and displayed it, but the template doesn't return a valid list/json to the second view in the POST parameters and I can't extract my authors. I don't know what to think:
else what I want to do is fine and I just have to better handle my data structure
else I shouldn't be doing like that and use more of Django: use more forms ? sessions ?
This is how I see it. It is not really an answer, but it's already too much code to put into comments
Template:
for b in result_list
h4
for aut in b.authors
div {{ aut }}
form(action='/add/', method='post')
{% csrf_token %}
input(type='hidden', value="{{ b.authors }}", name='authors')
input(type='submit', value="Add book")
View:
if request.method == 'POST':
authors = request.POST.get['authors']
#do something with authors, e.g. create a JSON string:
authors_JSON = json.dumps(authors)
Same logic, using Django forms:
View:
if request.method == 'POST':
book = BookForm(request.POST)
#do something with authors, e.g. create a JSON string:
authors_JSON = json.dumps(book.authors)
Forms.py:
class ContactForm(forms.Form):
authors = forms.CharField(max_length=100)
#etc, possibly including custom __init__ logic
That 'u' thing, it happens because you do json.loads() on an object. If you want to serialize, it should be json.dumps(), otherwise array converted to string and then treated as JSON, that is why Python unicode mark got there (i.e. not a template bug)
I finally got it: we can easily share data between views using the session.
The session is activated in the default Django install. We just have to choose a session engine: in a temporary file, in memory,…
SESSION_ENGINE = 'django.contrib.sessions.backends.file'
Now in my search/ url I can add a variable in request.session, a dictionnary-like object:
search_results = compute_and_get_data()
request = search_results()
I feed my template with the search_results list. In my template, I iterate on it and I use a single hidden input field to return the counter of the for loop.
for b in search_results
table
tr
td
h4 {{b.authors}}
form(action='/add/', method='post') {% csrf_token %}
{% csrf_token %}
input(type='hidden', value="{{ forloop.counter0 }}", name='forloop_counter0')
input.btn.btn-primary(type='submit', value='add book', title="add that book to my collection")
On submit, we enter the add/ view. Here I can get the book the user selected:
forloop_counter0 = int(request.POST["forloop_counter0"])
book = request.session["search_result"][forloop_counter0]
Now my book is a dictionnary with a lot of information. I didn't have to handle json formatting and the like.
This question helped me:
Django Passing data between views
How do you pass or share variables between django views?
I need to customize the admin panel added to the model page related inputs from another model. But I can not figure out how to save them.
admin.py
class OrderAdmin(admin.ModelAdmin):
change_form_template = 'admin/testapp/order/change_form.html'
def change_view(self, request, object_id, extra_context=None):
order = Order.objects.get(id=object_id)
card_list = Card.objects.all().filter(type=order.type)
result = super(OrderAdmin, self).change_view(request, object_id, extra_context={
'card_list': card_list,
})
return result
change_form.html
{% for card in card_list %}
<input type="text" name="card-{{ card.id}}" value="{{ card.qty }}"></td>
{% endfor %}
How to save the changed values in the Card model?
I tried to do as described here:
https://docs.djangoproject.com/en/1.3/ref/contrib/admin/#adding-custom-validation-to-the-admin
But self.cleaned_data does not include my data from inputs.
Thanks.
UPD: Well, I caught the data, but I think it's pretty messy way.
I'm hardly can imagine how I would calculate the id from inputs.
def save_model(self, request, obj, form, change):
request.POST['card-288']
Saving should be done in save_model. Your card_list data should also be would have been available in the form parameter if you had used django forms. Although you could still access it through request as you correctly pointed out.
You will also have to sanitize the data yourself, something that django forms does for your automatically.
You are getting exactly what you produced :), nothing unexpected there!
You must override clean (take a look here for instance) if you want some fields that you manually added in the change_list to be taken care of.
If you go that way, to retrieve the ID is up to you, but a simple split('-')[-1] would work.
This answers your specific need.
On the other side, I think your are not going the right way. The django admin is good enough to deal with ForeignKeys ManyToMany, so I see no need to do things yourself.
While you can get it working with the above hint, I suggest you start all over - if you want, a new SO question where you publish the initial Model and what exactly you are trying to achieve.