Django ModelForm iterate through MultipleChoiceField Values and process on POST - python

Level: Absolute Beginner, trying to build an app to perform some db operation through web UI
models.py
from django.db import models
class MysqlUser(models.Model):
username = models.CharField(max_length=100)
password = models.CharField(max_length=50)
environment = models.CharField(max_length=50)
forms.py
from django import forms
from onboard_app.models import MysqlUser
class MysqlUserForm(forms.ModelForm):
CHOICES = (
('test', 'Test'),
('develop', 'Develop'),
('staging', 'Staging'),
)
environment = forms.MultipleChoiceField(choices=CHOICES)
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = MysqlUser
fields = ('username', 'password', 'environment')
views.py
from django.shortcuts import render
from onboard_app.serializers import MysqlUserSerializer
from rest_framework import generics
from onboard_app.forms import MysqlUserForm
from onboard_app.models import MysqlUser
from django.views.generic.edit import CreateView, UpdateView, DeleteView
class MysqlCreateView(CreateView):
model = MysqlUser
form_class = MysqlUserForm
template_name = 'mysqluser_form.html'
success_url = '/mysql/user/add/'
mysqluser_form.html
{% extends "myapp/base.html" %}
{% block title %}MyApp{% endblock %}
{% block content %}
<h1>MySQL User Access</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" class="btn btn-primary" value="Grant Access">
</form>
{% endblock %}
I'm trying to get Value(s) of field or MultipleChoiceFiled environment after the user Form Submit, and loop through the entered values to perform some action. I have been trying this for long, still can't figure out how. I don't want to process anything in the frontend html. I'm thinking it has to be processed in the Views but not sure how to get the values of the field and loop over.
Any examples or any django concepts to look into will help me a lot. Any help is appreciated, thanks in advance!

Unless you have already done so, I recommend going through Django docs on class-based views (https://docs.djangoproject.com/en/3.0/topics/class-based-views/intro/) to get an overview of how the whole thing works.
So after you submit a form, a post() method of your view is called. CreateView provides a default implementation, which validates the user input using the MysqlUserForm you have provided and then creates an instance of MysqlUser and redirects to the success_url.
If you want to add more logic, you need to overwrite the post() method (or some other method, called by post(), in particular, the form_valid method) and put your logic there. To get a complete sense of how things work, I recommend you to read through the CreateView, BaseCreateView, ProcessFormView, and ModelFormMixin source code, although the inheritance looks a bit complicated (and it is). More to that, I really advise you to walk through the request processing from the View.dispatch method all way to the form_valid method using the debugger and see how things really work. Trust me, it will really contribute to your development skills improvement and understanding. Actually, I've discovered the form_valid method to write this answer by reading the source code (I use rest-framework nowadays and don't remember a lot about Django views).
So, what you need is
class MysqlCreateView(CreateView):
model = MysqlUser
form_class = MysqlUserForm
template_name = 'mysqluser_form.html'
success_url = '/mysql/user/add/'
def form_valid(self, form):
environment = form.cleaned_data['environment']
# insert your code here
return super().form_valid(form)
P.S. A good IDE like a PyCharm is really much much more convenient to read source code, jump to relevant parts and debug than any text editor and PDB debugger.

Related

Django ListView

It's my first time to use ListView and it doesn't work and give me error.
I put get_query but they still give me same error. How can I fix the problem?
And everytime when I write code in views.py I always used 'def' not 'class'. But could see many people use (and also django documents) 'class' for ListView. So for general render stuffs we use 'def' and for django.views.generic stuffs we use class right? Why they distinguished these two?
This is error what I got.
ImproperlyConfigured at /search/results
ListView is missing a QuerySet. Define ListView.model, ListView.queryset, or override ListView.get_queryset().
urls.py
from django.urls import path
from django.conf import settings
from django.views.generic import ListView, TemplateView
from . import views
app_name = 'search'
urlpatterns = [
path('', TemplateView.as_view(template_name = "search/index.html")),
path('results', ListView.as_view(template_name = 'search/results.html')),
path('beerlist', views.beerlist, name='beerlist'),
path('<int:beerinfo_id>', views.beerinfo, name='beerinfo'),
]
views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.db.models import Q
from django.views.generic import ListView, TemplateView
from .models import Beerinfo
# Create your views here.
def index(TemplateView):
template_name = 'search/index.html'
def results(ListView):
model = Beerinfo
template_name = 'search/results.html'
def get_queryset(self):
query = self.request.GET.get('q')
object_list = Beerinfo.objects.filter(
Q(name__icontains = query) | Q(label__icontains = query)
)
return obejct_list
index.html
<form action="{% url 'search:results' %}" name="se">
<label for='search'>What do you want to find?</label>
<input type="text" name='q'>
<input type="submit">
</form>
results.html
<ul>
{% for beer in ojbect_list %}
<li>{{ beer.name }}</li>
{% endfor %}
</ul>
models.py
from django.db import models
# Create your models here.
class Beerinfo(models.Model):
name = models.CharField(max_length=100)
label = models.CharField(max_length=500)
def __str__(self):
return self.name
You need to define the class that the list view will work with. For example:
class UserListView(ListView):
model = User
You can use a function (def) to accomplish the same thing that a generic view class, the difference is that most of what you write in the function is already defined in the class. In my example above, that class already handles the rendering of a default template, a context with the list of object of that template and pagination. The idea is to keep your code DRY.
The second advantage is that it creates a standard for your code, for example the default template to be used is
%(app_label)s/%(model_name)s%(template_name_suffix)s.html, so if your app name is users and your model is User, the this view expects a template named users/userlist.html
To use the pagiation simply set the paginate_by attribute of the class.
If you are trying to implement a simple view (for example all CRUD actions, then is very likely that you will benefit from using clases. Another good thing that classes give you, is that you can inherit goodies, for example, you can create a BaseListView class that inherits from ListView and set paginate_by to 25. If all your clases inherit from BaseListView then all your list will be paginated by 25 elements.
In views.py change def to class , you need to define a class to use Listview, Class Results(ListView). In urls.py, you are calling Listview , you should call views.Results.as_view()

How to use html form 'placeholder' property with standard view?

I am new to Django and am using Django's user auth package (django.contrib.auth) for user login, password reset, etc.
Now, while everything works just fine, on the logon form, I'd like to use the html-placeholder property. How can I use / populate this? I did find some answers (e.g. this one) but I do not understand where to place this code, how to extend the view / form or even the model (e.g. adding new fields) as this gets delivered with the standard package.
Right now, I have added the following:
forms.py
from django import forms
from django.contrib.auth.forms import AuthenticationForm
class LoginForm(forms.Form):
username = forms.CharField(label='username')
password = forms.CharField(label='password')
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
self.fields['username'].widget.attrs['placeholder'] = 'Username'
self.fields['password '].widget.attrs['placeholder'] = 'Password'
I am not sure what I need to do in urls.py or models.py or anywhere else for the code to be executed.
I found the following solution:
installed bootstrap4
using the following tag in my html:
{% bootstrap_field form.password field_class="field" placeholder="Password" show_label=False %}
I believe by adding the widgets to your forms init, when you instantiate the form in your template the placeholder should appear like intended. Did you just try rendering your form such as {{form}} in the template and see if it rendered it? I dont think bootstrap4 is necessary but it is a great tool.

Django: efficient template/string separation and override

I have a generic Django view that renders a template. The template is in an app which other projects will use. Importing projects will typically subclass the View the app provides. The View has a default template, which does a job with generic wording.
99% of the time, subclassing Views will want to only change the text, so rather than make them duplicate the template for the sake of altering non-markup wording, i'm looking for a way to allow users of the class to replace wording in the template in the most efficient way.
Options explored so far:
template partials containing only the text which using apps can override (magic, a lot of user work)
A template_strings method on the view which provides a dict of strings which end up in the template context which subclasses can override
Using (abusing?) the translation system such that the app provides default english translations and using code can provide their own translations instead (not actually worked this one out yet, just an idea)
Doing the above template_strings through AppConfig, but this seems ... yucky like it may get very unweildy with a lot of English strings. If doing this I would create a context-like setup so you don't have to re-declare all strings
Seems like it should be a solved problem to subclass a view which does a complete job and just provide alternate strings for text. Is there a better method than the above? Convention? Something I am missing?
(django 1.11 Python 3.6.2)
You can either inherit TemplateView or add ContextMixin to your view, and then override the get_context_data function like this:
from django.views.generic import TemplateView
class BaseView(TemplateView):
template_name = "common.html"
class SubView(BaseView):
def get_context_data(self, **kwargs):
context = super(SubView, self).get_context_data(**kwargs)
context['content'] = "Some sub view text"
return context
Update: Use template overriding
If you want to separate the text out, this is the better way to go
To allow easily and DRY override template across apps, you might need to install this package (Some other detail here)
We define it similarly as above, but change the template_name instead:
from django.views.generic import TemplateView
class BaseView(TemplateView):
template_name = "main.html"
# on another app
class SubView(BaseView):
template_name = "sub_view.html"
Then the magic is you can extends and override block of the BaseView template like this:
base_app/templates/main.html
<p>I'm Common Text</p>
{% block main %}
<p>I'm Base View</p>
{% endblock %}
sub_app/templates/sub_view.html
{% extends "base_app:main.html" %}
{% block main %}
<p>I'm Sub View</p>
{% endblock %}
The result would be:
<p>I'm Common Text</p>
<p>I'm Sub View</p>
Afaik you covered the options pretty well. My example is probably just a variant of the the template strings but maybe it helps anyway...
class DefaultStringProvider():
TITLE = 'Hello'
DESCRIPTION = 'Original description'
CATEGORY = 'Stuff'
class MyTemplateBaseView(TemplateView):
def get_context_data(self, **kwargs):
return super(MyTemplateBaseView, self).get_context_data(
provider=self.get_string_provider(), **kwargs)
def get_string_provider(self):
return DefaultStringProvider()
class OtherView(MyTemplateBaseView):
template_name = 'welcome.html'
def get_string_provider(self):
p = DefaultStringProvider()
p.TITLE = 'Hello'
p.DESCRIPTION = 'New description'
return p
The idea is to have a default string provider and the base view populates the context with it through get_string_provider().
It will at least be quite clear which strings can be overridden for a user extending the base class and it will not interfere with translations.

Django forms question

I don't have an example because I'm not working on anything relevant right now, but am still curious, after reading the docs about formsets:
What is a best practice for having a single view with multiple different model forms that post at the same time (rather 1 combined form, since you can't post multiple forms at the same time, but for lack of a better explanation...), some being single model forms, and others being 1-or-more formsets (e.g. Person, his 1 Address, and his 1 or more Pet objects), like Django does with TabularInline. Inlines have been in Django for some times, so my suspicion is that there are better practices than what I may find by simply copy/pasting what's in admin/options.py, no?
Thanks in advance
You should:
Make sure you're using transactions (so, make sure they're turned on, and that you're using something other than MySQL with MyISAM tables). This is true all the time, really, but it's even more true now. :)
Use multiple forms.Form/forms.ModelForm objects, which are grouped together in a single HTML <form> element, such as...
Python:
from django import forms
class FormA(forms.ModelForm):
[...]
class FormB(forms.ModelForm):
[...]
HTML:
<form method="post" action="/path/to/view/">
{% csrf_token %}
{{ form_a }}
{{ form_b }}
<input type="submit" value="Submit Form" />
</form>
Then, when you're processing your forms, simply process them both and make sure that you're requiring both to be valid to actually complete the view in a success case.
from django.db import transaction
from django.http import HttpResponseRedirect
from django.template.response import TemplateResponse
from myapp.forms import FormA, FormB
#transaction.commit_on_success
def present_forms_to_user(request):
if request.method == 'POST':
form_a = FormA(request.POST)
form_b = FormB(request.POST)
if form_a.is_valid() and form_b.is_valid():
# processing code
return HttpResponseRedirect('/path/to/thank/you/page/')
else:
form_a = FormA()
form_b = FormB()
return TemplateResponse(request, 'templates/eggs.html', {
'form_a': form_a,
'form_b': form_b,
})
As a disclaimer, remember that this is a basic example stub, and not meant to be copied blindly. Your ultimate use case for this may be slightly different, and that's fine.

Django admin - inline inlines (or, three model editing at once)

I've got a set of models that look like this:
class Page(models.Model):
title = models.CharField(max_length=255)
class LinkSection(models.Model):
page = models.ForeignKey(Page)
title = models.CharField(max_length=255)
class Link(models.Model):
linksection = models.ForeignKey(LinkSection)
text = models.CharField(max_length=255)
url = models.URLField()
and an admin.py that looks like this:
class LinkInline(admin.TabularInline):
model = Link
class LinkSectionInline(admin.TabularInline):
model = LinkSection
inlines = [ LinkInline, ]
class PageAdmin(admin.ModelAdmin):
inlines = [ LinkSectionInline, ]
My goal is to get an admin interface that lets me edit everything on one page. The end result of this model structure is that things are generated into a view+template that looks more or less like:
<h1>{{page.title}}</h1>
{% for ls in page.linksection_set.objects.all %}
<div>
<h2>{{ls.title}}</h2>
<ul>
{% for l in ls.link_set.objects.all %}
<li>{{l.title}}</li>
{% endfor %}
</ul>
</div>
{% endfor %}
I know that the inline-in-an-inline trick fails in the Django admin, as I expected. Does anyone know of a way to allow this kind of three level model editing? Thanks in advance.
You need to create a custom form and template for the LinkSectionInline.
Something like this should work for the form:
LinkFormset = forms.modelformset_factory(Link)
class LinkSectionForm(forms.ModelForm):
def __init__(self, **kwargs):
super(LinkSectionForm, self).__init__(**kwargs)
self.link_formset = LinkFormset(instance=self.instance,
data=self.data or None,
prefix=self.prefix)
def is_valid(self):
return (super(LinkSectionForm, self).is_valid() and
self.link_formset.is_valid())
def save(self, commit=True):
# Supporting commit=False is another can of worms. No use dealing
# it before it's needed. (YAGNI)
assert commit == True
res = super(LinkSectionForm, self).save(commit=commit)
self.link_formset.save()
return res
(That just came off the top of my head and isn't tested, but it should get you going in the right direction.)
Your template just needs to render the form and form.link_formset appropriately.
Django-nested-inlines is built for just this. Usage is simple.
from django.contrib import admin
from nested_inlines.admin import NestedModelAdmin, NestedStackedInline, NestedTabularInline
from models import A, B, C
class MyNestedInline(NestedTabularInline):
model = C
class MyInline(NestedStackedInline):
model = B
inlines = [MyNestedInline,]
class MyAdmin(NestedModelAdmin):
pass
admin.site.register(A, MyAdmin)
My recommendation would actually be to change your model. Why not have a ForeignKey in Link to LinkSection? Or, if it's not OneToMany, perhaps a ManyToMany field? The admin interface will generate that for free. Of course, I don't recommend this if links don't logically have anything to do with link sections, but maybe they do? If they don't, please explain what the intended organization is. (For example, is 3 links per section fixed or arbitrary?)
You can create a new class, similar to TabularInline or StackedInline, that is able to use inline fields itself.
Alternatively, you can create new admin templates, specifically for your model. But that of course overrules the nifty features of the admin interface.

Categories