user5617249
user5617249

Reputation:

What's the difference between FormView and CreateView

In the official documentation, formView is A view that displays a form. On error, redisplays the form with validation errors; on success, redirects to a new URL. createView is A view that displays a form for creating an object, redisplaying the form with validation errors (if there are any) and saving the object. //(maybe my English isn't good enough)So if I want to make a generic view that users to create a new category I should use createview over formview right? I wasn't sure, so I tried both ways and both of them gives me same error:as_view() takes exactly 1 argument (2 given)

With form view:

for adding category

class CategoryFormView(FormView):
    form_class = CategoryForm
    template_name = 'main/add_category.html'

    def get_success_url(self):
        return self.request.build_absolute_uri(reverse('category', args=[self.object.slug]))

    def get_context_data(self, **kwargs):
        context = super(CategoryFormView, self).get_context_data(**kwargs)
        # Add any extra context data needed for form here.
        return context

with createView

#for adding category
class CategoryCreateView(CreateView):
    model = Category
    form_class = CategoryForm
    template_name = 'main/add_category.html'

    def form_valid(self, form):
      self.object = form.save(commit=False)
      #setting
      self.object.save()
      return HttpResponseRedirect(reverse('category', args=[self.object.slug]))

    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
      return super(CategoryCreateView, self).dispatch(request, *args, **kwargs)
   

Finally this is my urls.py

url(r'^add_category/', views.CategoryCreateView.as_view, name='add-category'),

Edit: sorry it works, just forgot to put parenthesis next to url, but can you please tell me the difference between createview and formview? when should use what?

Edit2:

def index(request):

    categories = Category.objects.order_by('likes')[:5]
    latest_posts = Post.objects.all().order_by('-created_at')
    popular_posts = Post.objects.all().order_by('-views')
    hot_posts = Post.objects.all().order_by('-score')[:25]
    context_dict = {
        'latest_posts': latest_posts,
        'popular_posts': popular_posts,
        'hot_posts': hot_posts,
        'categories': categories
    }
    return render(request, 'main/index.html', context_dict)
#for single-post page
#we use uuslug 
def post(request, slug):
    single_post = get_object_or_404(Post, slug=slug)
    single_post.views += 1  # increment the number of views
    single_post.save()      # and save it
    context_dict = {
      'single_post' :single_post,
    }
  
    return render(request, 'main/post.html', context_dict)
#for category page
#we use slugfield this time 
def category(request, category_name_slug):
  context_dict = {}
  try:
    category = Category.objects.get(slug=category_name_slug)
    context_dict['category_name'] = category.name

    posts = Post.objects.filter(category=category)
    context_dict['posts'] = posts
    context_dict['category'] = category
  except Category.DoesNotExist:
    pass

  return render(request, 'main/category.html', context_dict)
  

Edit 3

urls.py

from django.conf.urls import url
from main import views
from django.core.urlresolvers import reverse
from views import *

urlpatterns = [
    url(r'^$', IndexView.as_view(), name='index'),

    #url(r'^add_post/', views.add_post, name='add_post'),
    url(r'^add_post/$', PostCreateView.as_view(), name='post-add'),

    url(r'^(?P<slug>[\w|\-]+)/edit/$', PostUpdateView.as_view(), name='post-edit'),
    url(r'^(?P<slug>[\w|\-]+)/delete/$', PostDeleteView.as_view(), name='post-delete'),


    url(r'^add_category/$', CategoryCreateView.as_view(), name='add-category'),
    url(r'^(?P<slug>[\w|\-]+)/$', PostDetailView.as_view(), name='post'),

    url(r'^category/(?P<category_name_slug>[\w\-]+)/$', CategoryDetailView.as_view(), name='category'),
    
]

Upvotes: 9

Views: 5515

Answers (1)

miki725
miki725

Reputation: 27861

Yes. You were missing function call in:

url(r'^add_category/', views.CategoryCreateView.as_view(), name='add-category'),

As for your other question, CreateView and FormView are pretty similar, except CreateView is more specific implementation of FormView.

FormView is really meant to handle:

  • display form on GET
  • display form with errors on POST
  • redirect to a different page when form has no errors

CreateView on the other hand is identical to FormView except it does this as well:

  • it is assumed it is dealing with a ModelForm form which implements save() method
  • before redirecting to a different page, the form's save() is called which creates the model
  • the created model is stored in self.object so that you can use it in get_success_url to generate a URL for the created object (e.g. redirect to object detail page)

Classy views is an awesome resource to inspect Django class-based-views. Here are links for both classes:

https://ccbv.co.uk/projects/Django/1.9/django.views.generic.edit/FormView/ https://ccbv.co.uk/projects/Django/1.9/django.views.generic.edit/CreateView/

Check our the form_valid methods where you will see the biggest diff.

As for picking which to use, I think it all depends on your scenario. If you are trying to create model instances via ModelForm then CreateView is probably better for you and if you are doing something generic then FormView is probably a better fit.


class IndexView(TemplateView):
    template_name = 'main/index.html'

    def get_context_data(self, **kwargs):
        context = super(IndexView, self).get_context_data(**kwargs)
        context.update({
            'latest_posts': Post.objects.all().order_by('-created_at'),
            'popular_posts': Post.objects.all().order_by('-views'),
            'hot_posts': Post.objects.all().order_by('-score')[:25],
            'categories': Category.objects.order_by('likes')[:5],
        })
        return context

class PostDetailView(DetailView):
    model = Post
    context_object_name = 'single_post'
    template_name = 'main/post.html'
    # note that these are already "slug" by default
    pk_url_kwarg = 'slug'
    slug_field = 'slug'

    def get_object(self, queryset=None):
        obj = seuper(PostView, self).get_object(queryset)
        obj.views += 1
        obj.save()
        return obj

class CategoryDetailView(DetailView):
    model = Category
    template_name = 'main/category.html'
    context_object_name = 'category'
    # note that these are already "slug" by default
    pk_url_kwarg = 'slug'
    slug_field = 'slug'

    def get_context_date(self, **kwargs):
        context = super(CategoryDetailView, self).get_context_data(**kwargs)
        context.update({
            # really no need to put category_name in context
            # as you can simply do {{ category.name }} in your template

            # you can use relations to query related data
            'posts': self.object.post_set.all()
        })
        return context

Upvotes: 17

Related Questions