Thanks in advance for any help. I am new to django specifically as well as web development in general. I have been trying to teach myself and develop a website using the Django framework and while everything is working so far, I am not sure if I am really doing things in the best possible way.
Typically, within my django app, I will have certain points where I want to modify the contents of my database model in some way. A typical use case is where I have button on my site that says "Add a post":
models.py:
from django.db import models
# data model import
from django.contrib.auth.models import User
# Create your models here.
class Post(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
post = models.CharField(max_length=1024)
urls.py:
from django.urls import include, path
from . import views
urlpatterns = [
path('', views.post, name='new_post'),
path('post_exec', views.post_exec, name='post_exec'),
]
views.py:
from django.contrib import messages
from django.shortcuts import render, redirect
# import data models
from django.contrib.auth.models import User
from .models import Post
def post(request):
# make sure the user has privileges
user = User.objects.get(id = request.user.id)
if not user.is_superuser:
return redirect('home')
return render(request, 'post.html')
def post_exec(request):
# make sure the user has priveledges
user = User.objects.get(id = request.user.id)
if not user.is_superuser:
return redirect('home')
# create the post
new_post = Post.objects.create()
new_post.user = user
new_post.post = request.POST['post']
new_post.save()
# this could redirect to a confirmation page or something too
return redirect('home')
You can see what I am doing here is that everytime I need a change to the database due to user input, I am executing the code to do the requested database change in a django view and then redirecting to another page following completion of the database change. While this works, it feels kind of clunky (you end up needing pages that you never actually use), it seems pretty sketchy in terms of security (although I am checking for credentials on each page), and I imagine there is a better way to do this.
Can any experience Django developers offer some advice for how I should be best structuring my project for these specific types of common database operations?
The Django's views is a good place to organize the project's CRUD system so users can manage their data. You can use the class-based views to group the GET, POST etc requests. Also there are better ways of using the authorization system with the login_required() decorator, the LoginRequiredMixin class and other solutions that you can rich here
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'm using Django 1.8 to create an application and I'm using django_comments application to handle commenting system. I want only authenticated users to be able to write a new comment. So I've excluded name, email and url fields in forms.py since I want them to be hidden and retrieve these info automatically from logged in users:
class CommentDetailsForm(CommentSecurityForm):
exclude = ('name', 'email', 'url')
comment = forms.CharField(...)
But there's a function in CommentDetailsForm class to save these info in a dictionary to show them in Admin area:
def get_comment_create_data(self):
return dict(
content_type=ContentType.objects.get_for_model(self.target_object),
object_pk=force_text(self.target_object._get_pk_val()),
user_name=self.cleaned_data["name"],
user_email=self.cleaned_data["email"],
user_url=self.cleaned_data["url"],
comment=self.cleaned_data["comment"],
submit_date=timezone.now(),
site_id=settings.SITE_ID,
is_public=True,
is_removed=False,
)
My question is: How can I fill user_name, user_email and user_url in this function automatically when a logged in user post a new comment.
I guess one solution may be put something like <input type="hidden" name="user_name" value="{{ request.user.username }}" /> in template, but I'm not sure how to do it.
I have installed the django-registration app to my project. After a successful log in step, I am redirecting the user to localhost:8000/ - this is my default testing host and port. And I am displaying somewhere on the page, the username of the logged in user.
What I want to do now is that when I click the username some options like edit profile or change password will appear. My questions are the following:
Should I create another model (inside another new app) containing fields like profile photo, gender, birthday etc and add a foreign key to the User model from django.contrib.auth.models ? Or should I modify the model from django-registration to add some additional fields but which I do not ask for at registration phase and only update them later?
if I want my profile edit feature to be at /accounts/edit, which would be the best practice to do it? to edit the URLconf of my project and add a line like (r'^accounts/edit$',.....) just before (r'^accounts/', include('registration.backends.default.urls')), ?
I hope I made myself clear. I'm trying to figure out which would be the best approach before coding, as I am new to Django... Thanks
I find it's easier to decouple the profile table from the auth table. Just like you mentioned you can use a foreign key relationship to link that profile to the user. You can also apply a lambda inside of your profile table to automatically create a profile when a new user object is created.
Inside your template you can link to the profile page dynamically based on the current authenticated party by using
{% if request.user.is_authenticated %}
Update Profile
{% endif %}
user_profile being the name of your app which holds your user_profile table. That way when the request is made you use the regular expression for the current user id (similar to the polls example provided by django) to get the id number of the currently logged in user than inside the views you just query the database for that particular user.
views.py
def myView(request, user_id):
userProfile = UserProfile.objects.get(user.pk=user_id)
This is a high level example to give an idea of one way to accomplish it.
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.