I have a ModelForm with a FilteredSelectMultiple widget
from django import forms
from django.contrib import admin
class PortFolioForm(forms.ModelForm):
class Meta:
model = PortFolio
fields = ['title', 'description', 'keywords']
class Media:
css = {'all':['admin/css/widgets.css']}
js = ['/admin/jsi18n/']
keywords = forms.ModelMultipleChoiceField(label='Mots clés', widget=admin.widgets.FilteredSelectMultiple('Mots clés', False), queryset=Keyword.objects.all())
I'm using the form outside the admin, inside a view
c = {'form': PortFolioForm() }
return render(request, 'portfolio.html', c)
......
form = PortFolioForm(request.POST)
if form.is_valid():
form.save()
......
{{ form | crispy }}
When I'm already logged in as admin, the widget is dispayed as normal
If not it does not appear
I get the following js errors :
Refused to execute script from 'http://localhost/admin/login/?next=/admin/jsi18n/' because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled.
Uncaught ReferenceError: interpolate is not defined
at Object.init (SelectFilter2.js:38)
at SelectFilter2.js:233
at NodeList.forEach ()
at SelectFilter2.js:231
As the website I'm working on is meant to be used by other users, I would like to use just the widget itself, because all the saving is done within the views, using form.save()
This could be because you are accessing a script under the admin url:
class Media:
css = {'all':['admin/css/widgets.css']}
js = ['/admin/jsi18n/']
You need to access the js outside the /admin/ location. Either copy the script to a folder not under admin/ and then change the location, or provide an url not under admin. For instance add to urls.py:
url(r'^jsi18n/$',
'django.views.i18n.javascript_catalog',
name='jsi18n')
And then change the form script url to just:
class Media:
css = {'all':['admin/css/widgets.css']}
js = ['jsi18n/']
Check Django's FilteredSelectMultiple widget only works when logged in
Related
I have created a mini project called ToDoList App. I have used class based views for create, update and delete functions. There is MyTasks icon on navbar.
What I want?
If the user is not logged in and he clicks on MyTasks icon, he should be redirected to the login page and after login to MyTasks page.
I have used LOGIN_REDIRECT_URL = 'home' and LOGIN_URL='login' in settings.py file
If I write LOGIN_REDIRECT_URL = 'task_list', it is working fine else not.
Below is the code from views.py file:
class TaskListView(LoginRequiredMixin, ListView):
"""List of all tasks."""
model = MyTasks
context_object_name = 'tasks'
template_name = 'task_list.html'
This is from urls.py file:
path('tasks/', views.TaskListView.as_view(), name="task_list"),
I have used the attribute redirect_field_name='next' and tried to override dispatch method in the above class, still it is not working.
The output I am getting is, after login, it is getting redirected to homepage instead of my tasks page. Any help is much appreciated.
Below is the screenshot from the terminal:
[1]: https://i.stack.imgur.com/KtsIG.png
You should use absolute path instead of reverse url.
if you want to use reverse urls you need to use reverse function.
for example:
from django.urls import reverse
LOGIN_REDIRECT_URL = reverse("app_name:url_name")
but this is a global setting and will work for all of your views.
if you want to do it just for one of your views you can do it:
class YourView(LoginRequiredMixin, View):
login_url = '/login/' # defaults to settings.LOGIN_URL
redirect_field_name = 'redirect_to' # querystring name => ? redirect_to=current_url
Im trying to use Django Suit's form includes to add a button to the list display of all my subscribers in the admin. In the documentation it says to add this to your admin.py file in the right app.
class SubscriberAdmin(admin.ModelAdmin):
list_display = ('email', 'date')
readonly_fields = ('email', 'date')
def has_add_permission(self, request):
return False
suit_form_includes = (
('admin/suit_includes/suit_csv.html', 'top'),
)
However this only appears when clicking into an instance of an object, and doesn't show up on the admin page that shows the entire list of objects in the database. Is there a way to do this with Django Suit ? I had trouble finding anything on google.
suit form include template:
<button class="btn btn-info">Export to File</button>
Admin instance display (Where its appearing):
Admin list display (Where I want it to appear):
What's doing django-suit, here, is that it is including your HTML snippet in the change_form that is displayed for your model, the change_form being where you can modify your model and save the changes into the database.
Where you want it to appear is the "change_list", aka the place where you can see all of your instances of that model in your database.
To add it your html snippet, you should extend your change_list.html with your own snippet : More info on expanding templates in the official documentation
Good luck !
I am fairly new to Django and class based forms, and I am having trouble understanding how these interact with each other. Following from the django project example, I have tried to build a "search form", which would sit on all pages of my project:
# forms.py
from django import forms
class SearchForm(forms.Form):
myquery = forms.CharField(max_length=255,label="", help_text="sq")
def __unicode__(self):
return self.myquery
# views.py
from searchapp.forms import SearchForm
from django.views.generic.edit import FormView
from django.views.generic import TemplateView
class SearchView(FormView):
template_name = 'index.html'
form_class = SearchForm
success_url = '/searchres/'
def form_valid(self, form):
thequery=form.cleaned_data.get('myquery')
return super(SearchView, self).form_valid(form)
class Meta:
abstract = True
class SearchResView(SearchView):
template_name = 'searchres.html'
#urls.py
from django.conf.urls import patterns, include, url
from django.conf import settings
from deals.views import IndexView
from searchapp.views import SearchView, SearchResView
urlpatterns = patterns('',
url(r'^index/', SearchView.as_view(),name="home"),
url(r'^searchres/', SearchResView.as_view(),name="searchresx"),
)
The plan is the start off with a simple form for user to enter the search query, and also show the input form on the results page. I have the following questions here (sorry - I am a Django newbie esp. to Class Based Views):
How does one pass data ("thequery") to the success_url? i.e I would like success_url to have access to "thequery" so that I can use something like {{thequery}} on my template tags.
Upon submitting the form(name="home"), I see POST data from the form on my firebug, but I am able to see just "myquery" rather than "thequery". How does one use get_context_data() here to add/post "thequery" variable aswell?
Finally, I was wondering if it would be possible to construct the success_url based on "thequery" string i.e something like success_url = '/searchres/?q=' + thequery
Thank you in advance - I am hoping to learn more.
I would suggest using function based views for this. If you choose to subclass a generic view you will need to dig through a lot of documentation and possibly source code, to find the right methods to override. (If you're really keen then look at the ListView class along with the get_queryset(), get() and post() methods)
A single django view will normally handle both rendering the empty form AND processing the submitted form.
So the search page (both the form and the results), live at http://your-site.com/search. Your url conf is -
urlpatterns = patterns('',
#...
(r'^search/$', 'searchapp.views.search'),
)
And your view looks something like this -
def search(request):
if request.method == 'POST':
form = SearchForm(request.POST)
if form.is_valid():
my_query = form.cleaned_data['myquery']
object_list = YourModel.objects.filter(# some operation involving my_query)
return render_to_response('search_results.html', {'object_list': object_list})
else:
form = SearchForm()
render_to_response('search_form.html', {'form': form})
(Note I've assumed your form method is post rather than get - I know this isn't great http but it's a common pattern with django)
To respond to your questions -
Don't use your own method for cleaning data. Add a clean_myquery method to your form and access it with form.fields['myquery'].clean() (or if you've called is_valid() on your form, it's accessible with just form.cleaned_data['myquery']).
You want to try and avoid passing data for processing to the template. Do as much processing as you can in the view, then render the template. However if you want to pass myquery as a string for the template to render, then add it in to the context dictionary (the second non-key-word argument) in render_to_response -
return render_to_response('search.html', {'object_list': object_list, 'myquery': my query})
The post data is constructed from the form fields. You don't have a form field thequery. The view is processing the POST data - it's not creating it that's done by the html (which in turn is constructed by the Form class). Your variable thequery is declared in the view.
Django's URL dispatcher ignores query strings in the URL so http://your_site.com/ssearch will be processed by the same view as http://your_site.com/search?myquery=findstuff. Simply change the html in the template from <form method='post'> to and access the data in django with request.GET. (You'll need to change the code from the view I described above to include a new check to see whether you're dealing with a form submission or just rendering a blank form)
Have a good read of the docs on views, forms and the url dispatcher.
I have a form created in mysite/new_player.html. It accepts 3 fields, user_name, real_name, and site_played that correspond to the Player table in the database.
<h1> New Player </h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="/stakeme/new/" method="post">
{% csrf_token %}
User Name: <input type="text" name="user_name" id="user_name"/><br>
Real Name: <input type="text" name="real_name" id="real_name"/><br>
Site Played: <input type="text" name="site_played" id="site_played"/><br><br>
<input type="submit" value="New Player" />
</form>
I am stuck on how to add this to my mysite/views.py file. I have gone through the polls tutorial, but the only form that is used in the tutorial is a multiple choice "choice" of the "poll" and I can't seem to adapt that to text fields.
def new_player(request):
return render_to_response('stakeme/new_player.html',
context_instance=RequestContext(request))
So as I understand it, I would need to create something like def add(request): return render_to_response('stakeme/new/'.. etc and add the POST data in here, but that's where I am lost. I am not sure how to get the data into the database.
I am reading the Django docs, but I feel like I am just compounding something that I do not understand. If someone could point me in the right direction, I would really appreciate it.
Firstly, you don't need to define a new view to process the form data. Also, you are creating your form directly in HTML - it's possible to work this way (see later section of post) but it's better (easier) to use the Django Forms library.
Using Django Forms
The documentation (v1.3 forms documentation) from the start up to and including ''Displaying a form using a template'' explains the basics of using the forms library, so I'll copy & paste liberally from there. I'll also assume that you're familiar with basic python constructs & have Django 1.3 installed. Without further ado, here's my adhoc forms tutorial.
Start a new django project:
$ django.admin.py startproject mysite
Add a new app:
$ ./mysite/manage.py startapp myapp
Lets create our contact form (modified from example in Django forms doc). Create a file in side the myapp/ directory called called forms.py and put the following in it:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField(max_length=100)
Next, since you mentioned storing data from received contact forms in a database, we'll add a model, Feedback, to track received contact forms. In your models.py file, add the following:
class Feedback(models.Model):
subject = models.CharField(max_length=100)
message = models.TextField()
sender = models.CharField(max_length=100)
def __unicode__(self):
return "Subject:{subject}\nSender:{sender}\n{msg}".format(subject=self.subject,
sender=self.sender,
msg=self.message)
(You may notice this is very similar to the form we defined earlier; normally in a scenario like this, one would use Django model forms to create a form directly from a model, but we are building our forms by hand as a learning experience)
We also need to get Django to create the required table in our database for this Feedback model, so at the top of your settings.py insert the following useful code:
import os
PROJECT_DIR = os.path.dirname(__file__)
And change the DATABASES setting in settings.py to the following to use a sqlite database:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'NAME': os.path.join(PROJECT_DIR, "sqlite.db").replace('\\', '/'), # Or path to database file if using sqlite3.
'USER': '', # Not used with sqlite3.
'PASSWORD': '', # Not used with sqlite3.
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
'PORT': '', # Set to empty string for default. Not used with sqlite3.
}
}
Finally, change the INSTALLED_APPS setting to the following to include our recently created application myapp in the list of installed applications for mysite:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'myapp',
# Uncomment the next line to enable the admin:
# 'django.contrib.admin',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
)
Now run the syncdb command to get Django to create the tables in your sqlite database (which, since it's sqlite, will be created if it doesn't exist yet):
$ ./mysite/manage.py syncdb
(Django will prompt you to create a superuser as well: you don't have to create a superuser now since we don't need it and you can use django-admin.py createsuperuser to create one when you need it, but you can create now now if you like)
Now we need a view to display the contact form, and a view to thank people for submitting it. In your views.py file, add the following (modified slightly from Django forms docs):
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.template import RequestContext
from myapp.forms import ContactForm
from myapp.models import Feedback
def thanks(request):
return render_to_response('thanks.html')
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
sender = form.cleaned_data['sender']
feedback = Feedback(subject=subject, message=message, sender=sender)
feedback.save()
return HttpResponseRedirect(reverse('thanks')) # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
}, context_instance=RequestContext(request))
Now we need to map URLs to views. Open mysite/urls.py and make it look like the following
from django.conf.urls.defaults import patterns, include, url
# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
# admin.autodiscover()
urlpatterns = patterns('',
# Examples:
url(r'^thanks/$', 'myapp.views.thanks', name='thanks'),
url(r'^$', 'myapp.views.contact', name='contact'),
# url(r'^mysite/', include('mysite.foo.urls')),
# Uncomment the admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
# url(r'^admin/', include(admin.site.urls)),
)
Now we need some templates to display the contact form & the thankyou page. Create a directory mysite/templates/, create a file contact.html inside it, and put the following in it:
<html>
<head>
<title>Contact Us</title>
</head>
<body>
<p>Please fill out the following information and click submit:</p>
<form action="{% url contact %}" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
</body>
</html>
Also create a thanks.html page for the thank you page, and put the following in it:
<html>
<head>
<title>Thanks</title>
</head>
<body>
<p>Thank you. Your feedback is important to us</p>
<p>Please leave some more feedback at the Contact page</p>
</body>
</html>
Next, we need to make sure Django can find our templates, so modify the TEMPLATE_DIRS in mysite/settings.py setting to the following:
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
os.path.join(PROJECT_DIR, "templates").replace('\\', '/'),
)
Now, (finally!), you can run the debug server and test that everything works:
$ ./mysite/manage.py runserver 8080
Go to http://localhost:8080/ and try to enter some feedback. When you click Submit, it should put your entered details into the database & show the thank you page. You can check the details are entered into the database:
$ ./mysite/manage.py shell
Into the shell, type:
>>> from myapp.models import Feedback
>>> for f in Feedback.objects.all(): print f
(note that you need to press enter twice after entering the last line)
You should see the feedback entries you have created.
Creating forms manually in HTML
If you insist on doing this, you can access the form's request variables directly in your view using the request.POST dictionary, and then instantiating a model of your object manually & calling save (like in the contact() view function above).
I would not recommend doing this, because you lose a whole bunch of nice features that Django Forms provides (CSRF protection, validation, etc).
Other Tutorials
Since the original form of this question asked for some tutorials: the official Django wiki has a page listing some tutorials, some of which deal with forms. Be aware that a lot of those tutorials are quite old (mostly from 2007-2009).
Sounds like you want to have a good and thorough look at the
Getting started guide https://docs.djangoproject.com/en/dev/intro/
Forms documentation https://docs.djangoproject.com/en/dev/topics/forms/
After which you may come up with something along these lines. Untested and quickly scribbled together, likely to be riddled with bugs.
models.py
from django.db import models
class Person(models.Model):
user_name = models.CharField(max_length=30)
real_name = models.CharField(max_length=30)
site_played = models.CharField(max_length=30)
forms.py
from django import forms
class PlayerForm(forms.Form):
user_name = forms.CharField(max_length=30)
real_name = forms.CharField(max_length=30)
site_played = forms.CharField(max_length=30)
views.py
def player_form(request):
if request.method == 'POST':
form = PlayerForm(request.POST)
if form.is_valid():
user_name = form.cleaned_data['user_name']
real_name = form.cleaned_data['real_name']
site_played = form.cleaned_data['site_played']
player = Player(user_name=user_name,
real_name=real_name,
site_played=site_played)
player.save()
# Redirect to a thanks page maybe?
else:
form = ContactForm()
return render_to_response('contact.html', { 'form': form,})
contact.html
... lots of fancy html ...
{{ form }}
... more fancy html
You need something like this (read about form fields validation yourself):
models.py:
from django.db import models
class Player(models.Model):
user_name = models.CharField()
real_name = models.CharField()
site_played = models.CharField()
forms.py:
from django import forms
MyForm(forms.Form):
user_name = forms.CharField()
real_name = forms.CharField()
site_played = forms.CharField()
views.py:
from forms import MyForm
from models import Player
def new_player(request):
#...
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
player = Player()
player.user_name = form.cleaned_data.get('user_name')
player.real_name = form.cleaned_data.get('real_name')
player.site_played = form.cleaned_data.get('site_played')
player.save()
#...
return render_to_response('stakeme/new_player.html',
context_instance=RequestContext(request))
UPDATE:
After you get the idea, you might want to have a look at WTForms library.
I am making use of django-registration and django-profile to handle registration and profiles. I would like to create a profile for the user at the time of registration. I have created a custom registration form, and added that to the urls.py using the tutorial on:
http://dewful.com/?p=70
The basic idea in the tutorial is to override the default registration form to create the profile at the same time.
forms.py - In my profiles app
from django import forms
from registration.forms import RegistrationForm
from django.utils.translation import ugettext_lazy as _
from profiles.models import UserProfile
from registration.models import RegistrationProfile
attrs_dict = { 'class': 'required' }
class UserRegistrationForm(RegistrationForm):
city = forms.CharField(widget=forms.TextInput(attrs=attrs_dict))
def save(self, profile_callback=None):
new_user = RegistrationProfile.objects.create_inactive_user(username=self.cleaned_data['username'],
password=self.cleaned_data['password1'],
email=self.cleaned_data['email'])
new_profile = UserProfile(user=new_user, city=self.cleaned_data['city'])
new_profile.save()
return new_user
In urls.py
from profiles.forms import UserRegistrationForm
and
url(r'^register/$',
register,
{'backend': 'registration.backends.default.DefaultBackend', 'form_class' : UserRegistrationForm},
name='registration_register'),
The form is displayed, and i can enter in City, however it does not save or create the entry in the DB.
You're halfway there - you've successfully built a custom form that replaces the default form. But you're attempting to do your custom processing with a save() method on your model form. That was possible in older versions of django-registration, but I can see from the fact that you specified a backend in your URL conf that you're using v0.8.
The upgrade guide says:
Previously, the form used to collect
data during registration was expected
to implement a save() method which
would create the new user account.
This is no longer the case; creating
the account is handled by the backend,
and so any custom logic should be
moved into a custom backend, or by
connecting listeners to the signals
sent during the registration process.
In other words, the save() method on the form is being ignored now that you're on version 0.8. You need to do your custom processing either with a custom backend or with a signal. I chose to create a custom back-end (if anyone has gotten this working with signals, please post code - I wasn't able to get it working that way). You should be able to modify this to save to your custom profile.
Create a regbackend.py in your app.
Copy the register() method from DefaultBackend into it.
At the end of the method, do a query to get the corresponding User instance.
Save the additional form fields into that instance.
Modify the URL conf so that it points to BOTH the custom form AND the custom back-end
So the URL conf is:
url(r'^accounts/register/$',
register,
{'backend': 'accounts.regbackend.RegBackend','form_class':MM_RegistrationForm},
name='registration_register'
),
regbackend.py has the necessary imports and is basically a copy of DefaultBackend with just the register() method, and the addition of:
u = User.objects.get(username=new_user.username)
u.first_name = kwargs['first_name']
u.last_name = kwargs['last_name']
u.save()
As described in my comment on Django Trac ticket I made a metaclass and mixin to allow multiple inheritance for ModelForm Django forms. With this you can simply make a form which allows registration with fields from user and profile models at the same time without hard-coding fields or repeating yourself. By using my metaclass and mixin (and also fieldset mixin) you can do:
class UserRegistrationForm(metaforms.FieldsetFormMixin, metaforms.ParentsIncludedModelFormMixin, UserCreationForm, UserProfileChangeForm):
error_css_class = 'error'
required_css_class = 'required'
fieldset = UserCreationForm.fieldset + [(
utils_text.capfirst(UserProfileChangeForm.Meta.model._meta.verbose_name), {
'fields': UserProfileChangeForm.base_fields.keys(),
})]
def save(self, commit=True):
# We disable save method as registration backend module should take care of user and user
# profile objects creation and we do not use this form for changing data
assert False
return None
__metaclass__ = metaforms.ParentsIncludedModelFormMetaclass
Where UserCreationForm can be for example django.contrib.auth.forms.UserCreationForm form and UserProfileChangeForm a simple ModelForm for your profile model. (Do not forget to set editable to False in your foreign key to User model.)
With django-registration backend having such register method:
def register(self, request, **kwargs):
user = super(ProfileBackend, self).register(request, **kwargs)
profile, created = utils.get_profile_model().objects.get_or_create(user=user)
# lambda-object to the rescue
form = lambda: None
form.cleaned_data = kwargs
# First name, last name and e-mail address are stored in user object
forms_models.construct_instance(form, user)
user.save()
# Other fields are stored in user profile object
forms_models.construct_instance(form, profile)
profile.save()
return user
Be careful that registration signal is send at the beginning of this method (in method in superclass) and not at the end.
In the same manner you can make a change form for both user and profile information. Example for this you can find in my comment on Django Trac ticket mentioned above.
With registration 0.8 and later:
Create a subclass of registration.backends.default.views.RegistrationView in your views.py or equivalent:
from registration.backends.default.views import RegistrationView
class MyRegistrationView(RegistrationView):
form_class= MyCustomRegistrationForm
def register(self, request, **cleaned_data):
new_user= super(MyRegistrationView, self).register(request, **cleaned_data)
# here create your new UserProfile object
return new_user