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.
Related
I have a django CreateView where users can create new words. I also have an unrelated Song model, where users can choose to add words from the lyrics of the songs. So I've created a separate CreateView for this, so that I can have a different success url. The success url should go back to the song where the user was browsing. But I am struggling to figure out how to pass the pk of this particular object to the CreateView of a different model.
This is my special CreateView:
class CreateWordFromSong(LoginRequiredMixin, generic.CreateView):
template_name = 'vocab/add_custom_initial.html'
fields = ("target_word","source_word", etc.)
model = models.Word
from videos.models import Song
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super(CreateWordFromSong, self).form_valid(form)
success_url = reverse_lazy('videos:song-vocab', kwargs={'pk': song_pk) #how to get song_pk?
Everything works when I replace song_pk with the actual pk.
I overwrite form_valid so that the user can be saved with the new object. Perhaps there is a way I can alter this so that I can also get the song_pk? I've played around with it a bit, but without luck.
I get the song_pk from my url:
path('song/<int:song_pk>/', views.CreateWordFromSong.as_view(), name='create-song'),
So I should have access to it. But when I try adding it to my view:
class CreateWordFromSong(LoginRequiredMixin, generic.CreateView, song_pk)
I get the error: name 'song_pk' is not defined.
You could add in class CreateWordFromSong instead success_url method get_success_url
def get_success_url(self):
return reverse_lazy('videos:song-vocab', kwargs={'pk': self.kwargs.get('song_pk')})
It will be work, if action in template with form be like this
<form action="{% url 'create-song' song_pk=song.pk %}" method="post">
(Surely a bit lately ...)
I faced a similar case but not sur this matches with your.
I have a list of Mapping objects (children) related to Flow object (parent and ForeignKey).
This list uses the Flow PK.
urls.py
path('flow/<int:pk>/mapping',
mappingColumnOneFlowListView.as_view(), name='mapping-details'),
On each line of Mapping objects, I got a "delete" button.
After deletion, I want to lead back to the list of the Flow Mapping but couldn't because the PK used in the view lead to the (deleted) Mapping.
Another solution would have consisted in leading to another URL without pk (eg. absolute_url in Model) but this was not what I wanted.
It's surely not the best way to achieve the goal but I found this:
in my DeleteView i added :
def get_success_url(self):
pk = self.kwargs['pk'] # Mapping's PK
flow_pk = MappingField.objects.filter(
pk=pk).first().fl_id.pk # fl_id.pk = Flow's PK (FK)
return reverse('mapping-details', kwargs={'pk': flow_pk})
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.
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.
Context
I'm handling a form in a python view. Basic stuff.
def index(request):
# Handle form.
if request.method == 'POST':
form = CustomForm(request.POST)
if form.is_valid():
# Do stuff
return HttpResponseRedirect('/thankyou/')
else:
form = CustomForm()
# Render.
context = RequestContext(request, {
'form':form,
})
return render_to_response('app/index.html', context)
This form is shown on multiple pages, and I've ended up having duplicates of the form-handling code in multiple functions in views.py, rendering different templates. (However, the template code for the form resides in the base template)
That's dumb, so I tried looking around for ways to prevent the repeat of code. I like the suggested use of python decorators in this Stackoverflow question. I also found an excellent explanation of python's decorators here.
Question
I'm having trouble with trying to write the decorator. I need to return a form after the first if statement, followed by executing another if statement. But in a python function, no code after a return function gets executed... Does this require something like a nested decorator..?
Suggestions? Non-decorator suggestions welcome.
This is not the answer to your main question but this info may be helpful to you or somebody.
The question with suggestion about decorators is pretty old. Started from 1.3 version django have class based views - i think this is what you are looking for. By subclassing views you can reduce duplication of code (code from django docs just for example):
# Base view
class MyFormView(View):
form_class = MyForm
initial = {'key': 'value'}
template_name = 'form_template.html'
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
# <process form cleaned data>
return HttpResponseRedirect('/success/')
return render(request, self.template_name, {'form': form})
Now you can create another views classes based on MyFormView view. Form processing code stays same, but you can change it of course:
class AnotherView(MyFormView):
form_class = AnotherForm
initial = {'key1': 'value1'}
template_name = 'form1_template.html'
# you dont need to redefine post here if code stays same,
# post from base class will be used
I am new to Django (1.5) and I am trying to do a basic POST form. I have a TemplateView that implements the form (passed to the template using get_context_data).
When the form fails for some reason (e.g. validation error), I want to show the form again, containing the data that the user has filled. When it succeeds, I want to redirect to a success page (e.g. the just-created item).
Here's what I've done so far:
class WriteForm(forms.Form):
subject = forms.CharField()
text = forms.CharField(widget=forms.Textarea)
# some other stuff
class WriteView(MailboxView):
# MailboxView extends TemplateView and defines some context
template_name = 'messages/write.html'
form_data = None
def post(self, request, *args, **kwargs):
# treat form data...
# lets make things simple and just assume the form fails
# I want to do something like that:
self.form_data = request.POST
# should I return something?
def get_context_data(self, **kwargs):
context = super(WriteView, self).get_context_data(**kwargs)
if self.form_data is None:
context['form'] = WriteForm()
else:
context['form'] = WriteForm(self.form_data)
return context
Thanks in advance!
Django already has a FormView that you might be able to use. If you want to see how it works, here's the code on GitHub.
If you want to write your own view instead of using the built in form view, you might also find it useful to look at the FormView in Django Vanilla Views, which has a simpler implementation.