I'm trying to build an import feature/form in the django admin interface for a specific model.
I have already found the following question on Stackoverflow, however as I am new to django, I have not been able to wire it all up. Import csv data into database in Django Admin
I guess I understand how to work with Django objects and how to use the CSV reader module, but I have a heck of a time putting it all together in Django.
what i tried so far is this:
models.py
class RfidTag(models.Model):
"""
Available RFID-Tags from Importfile
"""
system = models.DecimalField(
_('system'),
max_digits=4,
decimal_places=0,
)
tagId = models.DecimalField(
_('tag ID'),
max_digits=4,
decimal_places=0,
)
serial = models.CharField(
_('serial'),
max_length=10,
)
# forms.py #
class RfidImport(forms.ModelForm):
file_to_import = forms.FileField()
class Meta:
model = RfidTag
fields = ("file_to_import",)
def save(self, commit=False, *args, **kwargs):
form_input = RfidImport()
file_csv = self.cleaned_data['file_to_import']
csv.register_dialect('excel-new', delimiter=';', quoting=csv.QUOTE_NONE)
records = csv.reader(file_csv, dialect='excel-new')
for line in records:
self.system = line[0]
self.tagId = line[1]
self.serial = line[2]
form_input.save()
datafile.close()
admin.py
class RfidTagAdmin(admin.ModelAdmin):
list_display = ('system','tagId','serial')
actions = ['import_tags']
def get_urls(self):
urls = super(RfidTagAdmin, self).get_urls()
my_urls = patterns('',
(r'^import/$', self.admin_site.admin_view(import_tags))
)
return my_urls + urls
def import_tags(self, request, queryset):
return HttpResponseRedirect("./import")
import_tags.short_description = "Import new RFID tags"
pass
admin.site.register(RfidTag, RfidTagAdmin)
views.py
#staff_member_required
def import_tags(request):
if request.method == "POST":
form = RfidImport(request.POST, request.FILES)
if form.is_valid():
form.save()
success = True
context = {"form": form, "success": success}
return HttpResponseRedirect("../")
else:
form = RfidImport()
context = {"form": form}
return HttpResponseRedirect("../")
My question is, is admin action actually the right way? Is there a better way to achieve what I am trying? And how do I wire this up? I have yet to see the form, after I select the import action and click "go".
The admin is the right way, however i wouldn't be using an action for this, those are designed to function over a list of objects and you don't need that. For this case simply extend the admin/index.html template and add an href to your view. After that you create a normal form in which you do your processing
Related
i using django with sqllite i create a survey form with the help of django model and django forms but when i hit submit i see sometime data save twice in my database is it happen normally or something wrong with my code
from django.shortcuts import render, redirect
from django.contrib import messages
from django.template.response import TemplateResponse
from .forms import SurveyForm
def index(request):
if request.method == 'POST':
form = SurveyForm(request.POST)
if form.is_valid():
form.save()
return TemplateResponse(request, 'thankyou.html')
else:
return messages.error(request, 'Please fill the form to continue')
else:
form = SurveyForm()
return render(request, 'learnersform.html', {'form': form})
my forms.py
from django import forms
from django.forms import ModelForm, Textarea
from .models import teacher
class teachersform(forms.ModelForm):
class Meta:
model = teacher
fields = '__all__'
widgets = {'name': forms.TextInput(attrs={'placeholder':'Your Name'}),
'Q3': forms.TextInput(attrs={'placeholder':'Please write your subjects'}),
'Q4': forms.RadioSelect(),
'Q5': forms.RadioSelect(),
'Q6': forms.RadioSelect(),
'Q7': forms.RadioSelect(),
'Q8': forms.RadioSelect(),
'Q9':forms.Textarea(attrs={'placeholder':'Write Here.......'}),
'Q10':forms.Textarea(attrs={'placeholder':'Write Here.......'}),
}
my models.py
from django.db import models
# Create your models here.
class teacher(models.Model):
name = models.CharField(max_length=80, blank=True,)
state = models.CharField(max_length=50, choices=state_choice,)
Q3 = models.CharField(max_length=80, default=None, blank=False)
Q4 = models.CharField(max_length=80, choices=q4_choice, default=None, blank=False)
Q5 = models.CharField(max_length=80, choices=q5_choice, default=None, blank=False)
Q6 = models.CharField(max_length=80, choices=q6_choice, default=None, blank=False)
Q7 = models.CharField(max_length=80, choices=q7_choice, default=None, blank=False)
Q8 = models.CharField(max_length=80, choices=q8_choice, default=None, blank=False)
Q9 = models.TextField(default=None, blank=False)
Q10 = models.TextField(default=None,blank=False)
def __str__(self):
return self.state
Your issues is not related with sqlite but rather with the code in views.py. When you refresh your page after the form has been saved, the same form data getting resubmitted; and same data is getting saved multiple times.
It is recommended that after the form data is successfully saved, a return HttpResponseRedirect or redirect should be used rather than rendering using the HttpRequest.
I would rewrite the code as
def index(request):
if request.method == 'POST':
form = SurveyForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('thankyou/')
#return TemplateResponse(request, 'thankyou.html')
else:
return messages.error(request, 'Please fill the form to continue')
else:
form = SurveyForm()
return render(request, 'learnersform.html', {'form': form})
Add a route to the urls.py like
path( 'thankyou/', views.thankyou, name='thankyou')
Include another function thankyou in views.py
def thankyou(request):
return render(request, 'thankyou.html')
Update:
In case you don't want to use the urls.py route and the extra view function, then you can pass the path thankyou.html to the HttpResponseRedirect constructor. In this case, no need to create route in urls.py or the extra view function.
return HttpResponseRedirect('thankyou.html')
I would personally suggest you extract the data from the form before saving, you can then manipulate the data, or save in other locations as well:
Firstly you will know how the data will be sent through by the names given in the form, so you can build out a dictionary of values...
You will need to build out the 'form_data' section to get a complete dict that is relevant to your form.
form_data = {
"name": request.POST['name'],
"Q3": request.POST['Q3']
my_form = SurveyForm(form_data)
if my_form.is_valid():
new_form = my_form.save(commit=False)
"""
Now you can access any data using the dict method - new_form.name, new_form.Q3
etc. Even if you don't initially use it, you can leave this section blank but use
it in the future at some stage...
new_form.name for example will be the users name entered on the form.
new_form.Q3 will be the answer to question 3.
Although this may be a longer way of doing things, it will allow you to gather the
form data, before saving it. You can also build in additional security features
prior to database submission.
"""
if new_form.Q3 == "Some Specific Answer":
# Add all users to list who answer Q3 in a certain way
some_list.extend(new_form.name)
# Now your done manipulating data, or pushing it to be saved in other places, you can save the form to your database.
new_form.save()
else:
# Form was not valid
messages.error(request, 'Please fill the form to continue')
Not sure what the scope of your project is but the above way will allow you to easily grow its code into the future as you can manipulate that data, for example you could push users names into different collections based on question results etc.
I'm creating a questionnaire / survey, and have two forms (Model Form) built on the same model. These forms are called on separate views, but when saved they appear as separate users in the database. I'm not sure how to get them so save as the same user, I am already using the ' post = form.save(commit=False), post.user = request.user, post.save()' method to save the forms.
EDIT: Added in an attempt to save to the same instance
Model:
class QuizTakers(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
industry_choices = (
(1, 'Service'),
(2, 'Hospitality'),
(3, 'Wholesale/Retail'),
(4, 'Manufacturing'),
(5, 'Agriculture')
)
industry = MultiSelectField(choices=industry_choices, max_length=1, max_choices=1)
company_name = models.CharField( max_length=100)
email = models.EmailField(blank=True)
score = models.FloatField(default=0)
completed = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.company_name
Forms:
# Form for getting company name
class QuizTakerForm(forms.ModelForm):
class Meta:
model = QuizTakers
fields = ['company_name']
# Form for getting company industry
class QTIndustryForm(forms.ModelForm):
class Meta:
model = QuizTakers
fields = ['industry']
Views:
# view for getting company name
def start(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = QuizTakerForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
request.session['company_name'] = form.cleaned_data['company_name']
post = form.save(commit=False)
post.user = request.user
post.save()
# redirect to a new URL:
return HttpResponseRedirect('industry/')
# if a GET (or any other method) we'll create a blank form
else:
form = QuizTakerForm()
return render(request, 'ImpactCheck/start.html', {'form': form})
# view for getting industry
class IndustryView(FormView):
template_name = 'ImpactCheck/industry.html'
form_class = QTIndustryForm
success_url = '1/'
def get(self, request):
company_name = request.session['company_name']
this_user=QuizTakers.objects.filter(company_name=company_name).order_by('-timestamp').first()
form=self.form_class(instance=this_user)
company_name = request.session['company_name']
return render(request, 'ImpactCheck/industry.html', {'form': form, 'company_name': company_name})
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
post = form.save(commit=False)
post.user = self.request.user
post.save()
return HttpResponseRedirect('/1')
Firstly, in your def start(request) function, you should consider adding the ID to request.session instead of the company name. Something along the lines of
def start(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = QuizTakerForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
form.instance.user=request.user
form.save()
request.session['obj_id'] = post.id
# redirect to a new URL:
return HttpResponseRedirect('industry/')
Now you can use that id to get both the name of your company, as well as the object.
In your IndustryView(FormView), if you're having trouble with the form instances, it's better to use UpdateView instead of the FormView (Be sure to import UpdateView first)
class IndustryView(UpdateView):
template_name = 'ImpactCheck/industry.html'
model = QuizTakers
fields = ['industry']
success_url = '/1'
def get_object(self):
return QuizTakers.objects.get(pk=self.request.session.get('obj_id'))
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['company_name'] = QuizTakers.objects.get(pk=self.request.session.get('obj_id'))
return ctx
We use the get_context_data method since you need the company_name in your template. The get_object method in this view, tells django which object is to be updated. By default, it grabs the pk from the url (as a url parameter). But since we store our id in the session, we need to explicitly define this function.
Also, since we switched to UpdateView, you no longer need the QTIndustryForm either.
I am still learning programming and currently experimenting with Django.
Currently I am trying to build a task manager but when I try to add a new Tasks(def new_tasks) it should be related to TaskGroup, instead the task_group_id is related to NULL and I can't seem to figure out why. I believe the reason is some where in the views.py -> def new_tasks. Any help is gratefully appreciated.
The structure:
Project 1
Task Group 1
Tasks 1
Task 1
Task 2
Task 3
Tasks 2
Task 4
Task 5
Task Group 2
Tasks 3
Task 6
Project 2
Task Group 3
Tasks 4
Task 7
Task 8
Relating the Task Group to Project works but relating the Tasks to Task Group gives the value NUll.
image of database
Models.py
from django.db import models
from django.contrib.auth.models import User
class Project(models.Model):
"""A project the user is working on."""
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
"""Return a string representation of the model."""
return self.text
class TaskGroup(models.Model):
""" Title of a group of tasks """
project = models.ForeignKey(Project, null=True, on_delete=models.CASCADE)
title = models.CharField(max_length=100, blank=True, default='')
desc = models.CharField(max_length=300, default='')
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
""" Return a string representation of the model. """
return self.title
class Tasks(models.Model):
""" Will be the title/segment that will contain the task items """
task_group = models.ForeignKey(TaskGroup, null=True, on_delete=models.CASCADE)
title = models.CharField(max_length=100, default='')
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
""" Return a string representation of the model. """
return self.title
views.py
from django.shortcuts import render, redirect
from .models import Project, TaskGroup, Task, Tasks
from .forms import ProjectForm, TasksForm, TaskGroupForm
from django.contrib.auth.decorators import login_required
from django.http import Http404
from django.contrib import messages
# Delete
from django.core.exceptions import PermissionDenied
from django.urls import reverse
#login_required
def taskgroup(request, task_id):
"""Show a single task"""
taskgroup = TaskGroup.objects.get(id=task_id)
tasks = taskgroup.tasks_set.all()
# Make sure the task belongs to the current user.
project = taskgroup.project
if project.owner != request.user:
raise Http404
context = {'taskgroup':taskgroup, 'tasks': tasks}
return render(request, 'tasks/taskgroup.html', context)
#login_required
def task(request, task_id):
"""Show a single task"""
tasks = Task.objects.get(id=task_id)
# Make sure the task belongs to the current user.
project = tasks.project
if project.owner != request.user:
raise Http404
context = {'tasks':tasks}
return render(request, 'tasks/task.html', context)
#login_required
def new_project(request):
"""Add new project"""
if request.method != 'POST':
# No data submitted; create a blank form.
form = ProjectForm()
else:
# POST data submitted; process data.
form = ProjectForm(data=request.POST)
if form.is_valid():
new_project = form.save(commit=False)
new_project.owner = request.user
new_project.save()
return redirect('tasks:projects')
# Display a blank or invalid form.
context = {'form': form}
return render(request, 'tasks/new_project.html', context)
#login_required
def new_taskgroup(request, project_id):
""" Add new task """
project = Project.objects.get(id=project_id)
# Make sure the task belongs to the current user.
if project.owner != request.user:
raise Http404
if request.method != 'POST':
# No data submitted; create a blank form.
form = TaskGroupForm()
else:
# POST data submitted; process data.
form = TaskGroupForm(data=request.POST)
if form.is_valid():
new_taskgroup = form.save(commit=False)
new_taskgroup.project = project
new_taskgroup.save()
return redirect('tasks:project', project_id=project_id)
# Display a blank or invalid form.
context = {'project': project, 'form': form}
return render(request, 'tasks/new_taskgroup.html', context)
#login_required
def new_tasks(request, taskgroup_id):
""" Add new task """
taskgroup = TaskGroup.objects.get(id=taskgroup_id)
# Make sure the task belongs to the current user.
project = taskgroup.project
if project.owner != request.user:
raise Http404
if request.method != 'POST':
# No data submitted; create a blank form.
form = TasksForm()
else:
# POST data submitted; process data.
form = TasksForm(data=request.POST)
if form.is_valid():
new_tasks = form.save(commit=False)
new_tasks.taskgroup = taskgroup
new_tasks.save()
return redirect('tasks:taskgroup', task_id=taskgroup_id)
# Display a blank or invalid form.
context = {'taskgroup': taskgroup, 'form': form}
return render(request, 'tasks/new_tasks.html', context)
Forms.py
from django import forms
from .models import Project, TaskGroup, Task, Tasks
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = ['text']
labels = {'text': ''}
class TaskGroupForm(forms.ModelForm):
class Meta:
model = TaskGroup
fields = ['title', 'desc']
labels = {'desc': 'Description:','title': 'Title:'}
widgets = {'desc': forms.Textarea(attrs={'cols':80})}
class TasksForm(forms.ModelForm):
class Meta:
model = Tasks
fields = ['title']
labels = {'title': 'title:'}
urls.py
"""Defines URL patterns for tasks"""
from django.urls import path
from . import views
app_name = 'tasks'
urlpatterns = [
# Home page
path('', views.index, name='index'),
# Page that shows all projects.
path('taskmanager/', views.projects, name='projects'),
# Detail page for a single project.
path('taskmanager/<int:project_id>/', views.project, name='project'),
# Detail page for a single taskgroup.
path('taskmanager/task/<int:task_id>/', views.taskgroup, name='taskgroup'),
# Detail page for a single task.
path('taskmanager/task/<int:task_id>/', views.task, name='task'),
# Page for adding a new project
path('new_project/', views.new_project, name='new_project'),
# Page for adding new task group
path('new_taskgroup/<int:project_id>/', views.new_taskgroup, name='new_taskgroup'),
# Page for adding new task
path('new_tasks/<int:taskgroup_id>/', views.new_tasks, name='new_tasks'),
# Page for editing task
path('edit_task/<int:task_id>/', views.edit_task, name='edit_task'),
]
urlpatterns.extend(
[
path('delete/<int:task_id>/', views.delete_task, name='delete_task'),
]
)
Made a Mistake, fixed it.
new_tasks.taskgroup = taskgroup => new_tasks.task_group = taskgroup
thanks to Iain!
This is a very beginner-orientated question but I've been stuck on this issue all day.
I would like to load the data for a specific record and be able to save it (Submit button in a template) but i'm still trying to understand instances and the save method.
models.py
class model_essays(models.Model):
user = models.ForeignKey(User, default='1', on_delete=models.CASCADE)
title = models.CharField(max_length=100, default='')
date_added = models.models.DateTimeField(auto_now_add=True)
body = models.TextField()
def __str__(self):
return self.title
I understand the id is created automatically
forms.py
class frm_essays (forms.ModelForm):
class Meta:
model = model_essays
fields = ['title', 'date_added', 'body']
urls.py
urlpatterns = [
path('essay/<int:pk>', views.views_essay),
]
views.py {stuck here}
#login_required
def views_essay(request, pk):
if request.method == 'POST':
updatedForm = essay_detail(request.POST, instance=request.? {I want the ID of the essay})
if u_form.is_valid():
updatedForm.save()
messages.success(request, f'this essay has been updated')
return redirect('essay_detail')
else:
updatedForm = frm_essays(instance=request.{as above})
context = {
'updatedForm': updatedForm
}
return render(request, 'essay_detail.html', context)
On the decorator - I haven't gotten around to only allowing the users to view their own created essays, this would be the next large hurdle but not the issue I'm asking about.
Unless I am mistaken you are looking for the same ID as the pk (primary key). You have that passed in as an argument to your function.
You just need to query the instance from the DB.
def views_essay(request, pk):
essay = model_essays.objects.get(pk=pk)
if request.method == 'POST':
updatedForm = essay_detail(request.POST, instance=essay)
...
Note: essay will be None if the query based on the pk does not find an match in the DB.
I have a ModelChoiceField called outage_name. I also have a simple form that allows you to select the item from the list. The ModelChoiceField is pulled from a MySQL DB. This queryset is located in forms.py
outage_name = forms.ModelChoiceField(queryset = Outage.objects.filter(published = True)
The models.py is listed below.
from django.db import models
from django.contrib.auth.models import User
class Outage(models.Model):
outage_name = models.CharField(max_length=60, unique=True)
published = models.BooleanField()
def __unicode__(self):
return self.outage_name
class Detail(models.Model):
detail = models.CharField(max_length=60, unique=True)
user = models.ForeignKey(User)
outage = models.ForeignKey(Outage)
def __unicode__(self):
return self.outage
When I select from the list and submit the form I can't seem to figure out how to match outage = models.ForeignKey(Outage) that was selected on the list. To the correct outage_name. In my views.py I can hard code the id and it submits to the database and everything works fine.
def turnover_form(request):
if request.user.is_authenticated():
if request.method == 'POST':
form = TurnoverForm(request.POST)
if form.is_valid():
details = Detail.objects.get_or_create(
detail = form.cleaned_data['detail'],
user = request.user,
outage = Outage.objects.get(pk=1))
return HttpResponseRedirect('/turnover/user/')
else:
form = TurnoverForm()
variables = RequestContext(request, {'form': form})
return render_to_response('turnover_form.html', variables)
else:
return HttpResponseRedirect('/authorization/')
Any advice on how to match the id with the selected item would be appreciated. I'm sure my code is not very pythonic as I'm still learning.
outage = form.cleaned_data['outage'] # cleaned_data['outage'] is a model instance