How to access the Django forms.FileField file (*not models.FileField*)? - python

I'm running Django 1.7.
I have the following model form:
class DeckCreateForm(forms.ModelForm):
csv_file = forms.FileField(required=False)
class Meta:
model = Deck
fields = ['title', 'description']
Notice that the file field is not part of the model (and I would like to keep it this way). This file field is meant to provide an alternative means of constructing the model Deck.
I would like to know how to access the uploaded file. I looked in my media directory but it is not there. I tried adding a "upload_to" to the csv_file constructor but get an error:
TypeError: __init__() got an unexpected keyword argument 'upload_to'
EDIT:
I would like to know how to get this to work with a generic class based create view which makes use of the above model form - in views.py I have:
class DeckCreateView(CreateView):
model = Deck
form_class = DeckCreateForm
template_name = 'deck_create.html'
Specifically, how do I modify something like http://docs.djangoproject.com/en/1.7/topics/http/file-uploads to work with the above class based view. My urls.py file:
urlpatterns = patterns(
...
url(r"^deck/create/$", views.DeckCreateView.as_view(), name="deck-create"),
...
)
Is there a method which I can override in DeckCreateView to handle the file upload?

I've found that the Django documentation concerning file uploads can be a little difficult to understand for newer Django users. However, I think that the following link provides a very concise and easy to follow step by step process of setting up a file upload form.
Need a minimal Django file upload example
I believe you'll find everything you need there.
Edit
In response to the OP's edit and comment concerning class based views, I believe they can be clearer and arguably "cleaner" looking code than function based views. Here is a great link discussing CBV and FBV that includes a simple, but effective example of CBV.
http://www.datalifebalance.com/2014/04/django-file-uploads-with-class-based-views.html
Addendum to Edit
For the sake of completeness, and to limit the dependence of the answer on the above external link (which may disappear one day), we add a few more details. In order to achieve their objective, the OP could override the post method of DeckCreateView and save, __init__ of DeckCreateForm like so:
views.py:
...
class DeckCreateView(CreateView):
...
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect(self.success_url)
else:
return render(request, self.template_name, {'form': form})
forms.py
...
class DeckCreateForm(forms.ModelForm):
...
def __init__(self, post_data, files_data):
self.csv_file = files_data.get('csv_file', None)
return super(DeckCreateForm, self).__init__(post_data, files_data)
def save(self, *args, **kwargs):
deck = super(DeckCreateForm, self).save(*args, **kwargs)
self.handle_csv_file(self.csv_file, deck)
return deck
def handle_csv_file(f, deck):
...
for chunk in f.chunks():
...
...
Upon form submission a request is sent to DeckCreateView::post. The file handling occurs when DeckCreateForm::save is called.

Related

Django Class Based View - How to access POST data outside of post() method

I have a CBV based on a TemplateView
It has a post(self) method to access user input.
Also, it has a template_name property which should be populated with data from post().
No Django models or forms are used here.
Attempt to extract data within post method:
# views.py
class ReturnCustomTemplateView(TemplateView):
def post(self, *args, **kwargs):
chosen_tmplt_nm = self.request.POST.get('tmplt_name')
print(chosen_tmplt_nm) # correct value.
# how do I use it outside this method? (like bellow)
template_name = chosen_tmplt_nm
... or is there any other way I can get the data from request.POST without def post()?
You can override the get_template_names() method [Django-doc] which returns an iterable (for example a list) of template names to search for when determining the name of the template, so:
class ReturnCustomTemplateView(TemplateView):
def get_template_names(self):
return [self.request.POST.get('tmplt_name')]
def post(self, *args, **kwargs):
return self.get(*args, **kwargs)
I would however advise to be careful with this: a POST request could be forged, so a hacker could make a POST request with a different template name, and thus try to obtain for example the content of the settings.py file or another file that contains sensitive data.

UpdateView unable to process POST request - form is not callable?

According to the documenation on UpdateView this should be really simple and indeed the form is showing with the content from the database, but when clicking the submit button django displays a message saying:
'ProfileForm' object is not callable'.
Why would it need a callable form ? The form works well with CreateView for example so no problem there, dont get why it would complain now.
I have researched stackoverflow and search on google and indeed there are results, but none of them seem to apply to my situation as I dont see that im making any mistakes although clearly I apparently am according to django.
My code is as follows:
class PortfolioEditBase(UpdateView):
post_req = False
url_name = ''
def form_valid(self, form):
self.post_req = True
return super(PortfolioEditBase, self).get_form(form)
def get_context_data(self, **kwargs):
context = super(PortfolioEditBase, self).get_context_data(**kwargs)
context['post_req'] = self.post_req
context['profile_id'] = self.kwargs['profile_id']
return context
def get_success_url(self):
return reverse(self.url_name, args=self.kwargs['profile_id'])
class PortfolioEditGeneralInfo(PortfolioEditBase):
model = Profile
form_class = ProfileForm
url_name = 'plan:general-info-edit'
template_name = 'plan/portfolio/edit/general_info_edit.html'
Profile form has the following code:
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = ['company', 'exchange', 'ticker',
'investment_stage', 'investment_type']
widgets = {
'earnings_growth': Textarea(attrs={'cols': 1, 'rows': 2}),
}
This is the relevant urls.py code:
url(r'^portfolio/general-info/edit/(?P<profile_id>[0-9]+)$', views.PortfolioEditGeneralInfo.as_view(), name='general-info-edit'),
I dont think the error message django gives me makes any sense. How about giving an error message that says a little more about what the actual problem is. Using function based views this is pretty simple and works with few lines of code, but class based views are suppose to be "best practice". It seems to try to fetch the form data, but i dont get why it would have any problems with that and why it would call a from instead of fetch the data with request.POST.
Do anyone know what is wrong here ? So annoying when its suppose to be so simple. The other class based views ived used has been working almost without any problems.
The error in form_valid method. It should be form_vaild instaed of get_form:
def form_valid(self, form):
self.post_req = True
return super(PortfolioEditBase, self).form_valid(form)
get_form method expecting form class as argument. But you are passing form instance.

Django get custom queryset into ListView

I have a listview that I access in a pretty bog standard way to return all metaobjects.
#url
url(r'^metaobject/$', MetaObjectList.as_view(),name='metaobject_list'),
#ListView
class MetaObjectList(ListView):
model = MetaObject
I've recently added a search form that I want to scan my objects (I've got about 5 fields but I've simplified the example). What I'd like to do is re-use my MetaObjectList class view with my specific subset. I am guessing I need to override the get_queryset method but I'm not clear in how I get the queryset from my FormView into the listview. I mucked around a bit with calling the as_view() in the formveiw's form_valid function with additional parameters but couldn't get it to work and it seemed hacky anyway.
class SearchView(FormView):
template_name = 'heavy/search.html'
form_class = SearchForm
#success_url = '/thanks/'
def form_valid(self, form):
#build a queryset based on form
searchval=form.cleaned_data['search']
list = MetaObject.objects.filter(val=search)
#where to from here?
I also looked at trying to post the data from the form view over to the listview but that seemed like I'd need to re-write the form logic into the listview.
I'm on python 3.x and django 1.11.
I found what I feel is more elegant than the comment on the question:
My form valid now points to the list object's as_view method and passes the request and the queryset I want
def form_valid(self, form):
#build a queryset based on form
searchval=form.cleaned_data['search']
list = MetaObject.objects.filter(val=search)
return MetaObjectList.as_view()(self.request,list)
This hits the ListView as a post which I use to alter the queryset
class MetaObjectList(ListView):
model = MetaObject
queryset = MetaObject.objects.prefetch_related('object_type','domain')
def post(self, request, *args, **kwargs):
self.queryset = args[0]
return self.get(request, *args, **kwargs)
The only obvious change is using kwargs to make it a bit clearer. Otherwise this seems to work well.

Django FormPreview: Save form data to database

Probably a simple question but having trouble implementing a form preview page using django-formtools. I've configured everything per the docs. I'm stuck on what to add to the done() method to save the data to db.
forms.py
class JobForm(ModelForm):
class Meta:
model = Job
fields = ('title', 'category', 'company', 'website', 'description',)
class JobFormPreview(FormPreview):
def done(self, request, cleaned_data):
# add what here to save form data as object?
return HttpResponseRedirect('/success')
urls.py
...
url(r'^jobs/new/$',
JobFormPreview(JobForm),
name='job_form'),
...
Using the default templates. The form and preview both render fine, but obviously data doesn't save on submit. Tried self.form.save() per this answer but get an error save() missing 1 required positional argument: 'self'.
I appreciate any guidance.
Looking at the formtools code, it looks as if self.form is the form class, not the validated form instance. Therefore self.form.save() will not work. I've removed the suggestion to call self.form.save() from the linked answer.
I can't see a straight forward way to access the validated form in the done method. I suggest that you create the instance using the cleaned_data instead:
class JobFormPreview(FormPreview):
def done(self, request, cleaned_data):
job = Job.objects.create(**cleaned_data)
return HttpResponseRedirect('/success')

Django multi model best practice

Basically what I'm trying to achieve is a multi-model django app where different models take advantage of the same views. For example I've got the models 'Car' 'Make' 'Model' etc and I want to build a single view to perform the same task for each, such as add, delete and edit, so I don't have to create a seperate view for add car, ass make etc. I've built a ModelForm and Model object for each and would want to create a blank object when adding and a pre-populated form object when editing (through the form instance arg), with objects being determined via url parameters.
Where I'm stuck is that I'm not sure what the best way to so this is. At the moment I'm using a load of if statements to return the desired object or form based on parameters I'm giving it, which get's a bit tricky when different forms need specifying and whether they need an instance or not. Although this seems to be far from the most efficient way of achieving this.
Django seems to have functions to cover most repetitive tasks, is there some magic I'm missing here?
edit - Here's an example of what I'm doing with the arguments I'm passing into the url:
def edit_object(request, object, id):
if(object==car):
form = carForm(instance = Car.objects.get(pk=id)
return render(request, 'template.html', {'form':form})
What about using Class Based Views? Using CBVs is the best way in Django to make reusable code. For this example maybe it can be a little longer than function based views, but when the project grows up it makes the difference. Also remember "Explicit is better than implicit".
urls.py
# Edit
url(r'^car/edit/(?P<pk>\d+)/$', EditCar.as_view(), name='edit-car'),
url(r'^make/edit/(?P<pk>\d+)/$', EditMake.as_view(), name='edit-make'),
# Delete
url(r'^car/delete/(?P<pk>\d+)/$', DeleteCar.as_view(), name='delete-car'),
url(r'^make/delete/(?P<pk>\d+)/$', DeleteMake.as_view(), name='delete-make'),
views.py
class EditSomethingMixin(object):
"""Use Mixins to reuse common behavior"""
template_name = 'template-edit.html'
class EditCar(EditSomethingMixin, UpdateView):
model = Car
form_class = CarForm
class EditMake(EditSomethingMixin, UpdateView):
model = Make
form_class = MakeForm
class DeleteSomethingMixin(object):
"""Use Mixins to reuse common behavior"""
template_name = 'template-delete.html'
class DeleteCar(DeleteSomethingMixin, DeleteView):
model = Car
class DeleteMake(DeleteSomethingMixin, DeleteView):
model = Make
Just pass your class and form as args to the method then call them in the code.
def edit_object(request, model_cls, model_form, id):
form = model_form(instance = model_cls.objects.get(pk=id)
return render(request, 'template.html', {'form':form})
then just pass in the correct classes and forms in your view methods
def edit_car(request,id):
return edit_object(request, Car, CarForm, id)
each method knows what classes to pass, so you eliminate the if statements.
urls.py
url(r'^car/delete/(?<pk>\d+)/', edit, {'model': Car})
url(r'^make/delete/(?<pk>\d+)/', edit, {'model': Make})
views.py
def edit(request, id, model):
model.objects.get(id=id).delete()

Categories

Resources