What Form should I create to display these models? - python

I'm brand new to Django and I'm trying to create a simple web, so sorry if this seems quite trivial or simple. Currently I have these models:
class Fighter(models.Model):
name = models.CharField(max_length=50)
rating = models.DecimalField(default=1600, max_digits=8, decimal_places=2)
url = models.URLField(default='')
class Fight(models.Model):
member1 = models.ForeignKey(Fighter, related_name='fighter_1')
member2 = models.ForeignKey(Fighter, related_name='fighter_2')
I want to display a random fight and it's associated members every time I refresh the page, and then have a radio button (or something similar) that allows a user to choose who they think would win.
I think I need to use forms to do this, but after reading lots of Django documentation I have no idea about how to do this. Could someone please explain the best way for me to go about this!
I have been using class based views, is this the correct thing to do? If I haven't included enough code then please let me know.

To keep track of which fight instance is being referred to, I have used the URL to mention the fight id. A new fight instance will be created each time the user visits the (home) page. Then the view will redirect to the URL for that fight's form.
urls.py
from django.conf.urls import patterns, include, url
from fights.views import FightView, random_fight
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^$', random_fight, name='home'),
url(r'^fight/(?P<fight_id>\d+)/$', FightView.as_view(), name='fight_view'),
url(r'^admin/', include(admin.site.urls)),
)
forms.py
The form will be initialized with a dynamically created radio box with the names of fighters passed as a keyword argument.
from django import forms
from .models import Fighter
class FighterSelectForm(forms.Form):
def __init__(self, *args, **kwargs):
choices = kwargs.pop("choices", [])
super(FighterSelectForm, self).__init__(*args, **kwargs)
self.fields["fighter_choices"] = forms.ChoiceField(
widget=forms.RadioSelect,
choices=choices)
views.py
random_fight() creates a new Fight instance with two random Fighter members and redirects to the corresponding fight_view. FightView class overrides the get_form() function to pass the fighter names from the Fight object, which is in turn derived from the URL.
from django.shortcuts import render
from .models import Fight, Fighter
from .forms import FighterSelectForm
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.views.generic.edit import FormView
def random_fight(request):
fighters = Fighter.objects.order_by("?")[:2]
fight = Fight.objects.create(member1=fighters[0], member2=fighters[1])
return HttpResponseRedirect(reverse('fight_view', kwargs={"fight_id": str(fight.id)}))
class FightView(FormView):
template_name = 'fight.html'
success_url = '/thanks/' # Not defined here
def get_form(self, form_class):
fight = Fight.objects.get(id=self.kwargs.get("fight_id"))
choices = [(fight.member1.id, fight.member1.name),
(fight.member2.id, fight.member2.name)]
kwargs = super(FightView, self).get_form_kwargs()
kwargs.update({"choices": choices})
form = FighterSelectForm(**kwargs)
return form
def form_valid(self, form):
selected = Fighter.objects.get(id=form.cleaned_data['fighter_choices'])
selected.rating += 100 # Use any logic to change rating here
selected.save()
print("Fighter selected: {}".format(selected.name))
return super(FightView, self).form_valid(form)
fight.html
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>

Related

AttributeError at /delete/1/ Generic detail view DeleteTodo must be called with either an object pk or a slug in the URLconf [duplicate]

This question already has answers here:
Django: Generic detail view must be called with either an object pk or a slug
(6 answers)
Closed 1 year ago.
I'm starting with Django, and i wanna create simple projects to test my skills. First is the simple todo list. I've found a problem during trying to create 'delete task' functionality on my project.
views.py
from django.shortcuts import render, redirect
from django.urls import reverse_lazy
from .models import Task
from .forms import TaskForm
from django.views.generic import DeleteView
def index(request):
return render(request, 'todoxd_app/index.html')
def todo(request):
objekt = Task.objects.all()
context = {'objekt': objekt}
return render(request, 'todoxd_app/todo.html', context)
def new_todo(request):
if request.method != 'POST':
form = TaskForm()
else:
form = TaskForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('todoxd_app/new_todo')
context = {'form': form}
return render(request, 'todoxd_app/new_todo.html', context)
class DeleteTodo(DeleteView):
model = Task
template_name = 'registration/delete_todo.html'\
urls.py
from django.urls import path, include
from .views import DeleteTodo
from . import views
app_name = 'todoxd_app'
urlpatterns = [
path('', views.index, name='index'),
path('todo/', views.todo, name='todo'),
path('new_todo/', views.new_todo, name='new_todo'),
path('delete/<post_pk>/', DeleteTodo.as_view() ,name='delete_todo'),
]
delete_todo.html
Title
{% extends 'todoxd_app/base.html' %}
{% block content %}
{% csrf_token %}
DELETE TASK
{% endblock content %}
from django.db import models
class Task(models.Model):
name = models.CharField(max_length=50)
description = models.TextField()
date_added = models.DateField(auto_now_add=True)
date_end_of_task = models.CharField(max_length=20)
progress = models.BooleanField()
def __str__(self):
return self.name
I would be grateful for help (:
for deleting task you have to specify django which task you want to delete and for that you have to call an id or slug field which has to be uniques only for that task then you can delete task here is the way and i am using id here
def DeleteTodo(request, id):
task = Task.objects.filter(id=id)
task.delete()
return render(request, 'registration/delete_todo.html')
in your html in href tag by which you want to allow user delete that task
Delete It
and last in your url
path('<int:id>/delete', views.DeleteTodo, name='delete_todo'),
and now you can delete it
By default, detail views like DeleteView will look for the primary key in the url with the kwarg pk as explained in the docs. So if you want to support using post_pk in your view, set that as the value in pk_url_kwarg:
class DeleteTodo(DeleteView):
model = Task
template_name = 'registration/delete_todo.html'
pk_url_kwarg = 'post_pk'
Or a simpler way to fix this is change your url config to use pk instead:
path('delete/<int:pk>/', DeleteTodo.as_view(), name='delete_todo'),

Django class based view: passing additional information to the next view

I'm very new to Django and a bit overwhelmed by the documentation. I think my problem is pretty simple but everything i've found just confused me more.
I am building a little news app with a model NewsItem:
from django.db import models
from django.utils import timezone
# Create your models here.
class NewsItem(models.Model):
title = models.CharField(max_length=50)
newsText = models.TextField()
dateEntered = models.DateTimeField('date entered')
datePublished = models.DateTimeField('date published', blank=True, null=True)
user = models.CharField(max_length=30) #temporary field. will be changed to user foreign key
def __str__(self):
return self.title
def publish(self):
if (self.datePublished == None):
self.datePublished = timezone.now()
def published(self):
return self.datePublished != None
two views (technically 3) index and detail
from django.http import HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django.views import generic
from .models import NewsItem
# Create your views here.
class IndexView(generic.ListView):
template_name = 'news/index.html'
context_object_name = 'latestNewsList'
def get_queryset(self):
return NewsItem.objects.order_by('-datePublished')[:5]
#todo
class DetailView(generic.DetailView):
model = NewsItem
template_name = 'news/detail.html'
def publish(request, itemId):
newsItem = get_object_or_404(NewsItem, pk=itemId)
newsItem.publish()
newsItem.save()
return HttpResponseRedirect(reverse('news:detail', args=(newsItem.id,)))
and an urlconf like this
from django.urls import path
from . import views
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:itemId>/publish', views.publish, name='publish'),
]
In the detail view i have a link Publish which just triggers the function views.publish. This view is supposed to redirect back to the detail view.
What i'm trying to do now is to display a little message (like article successfully published) in detail view when it was redirected by the publish view. But i have no idea what would be a good aproach
I could just render the details template in the publish view, but then it would still say news/publish in the URL instead of news/detail
Thanks in advance for your help
Have a look at the messages framework. You could add a success message before redirecting, which will be displayed on the next page.
from django.shortcuts import redirect
from django.contrib import messages
def publish(request, itemId):
newsItem = get_object_or_404(NewsItem, pk=itemId)
newsItem.publish()
newsItem.save()
messages.success(request, "The post has been published")
return redirect('news:detail', newsItem.id)
Note that I've simplified the return statement to use redirect(...) instead of HttpResponseRedirect(reverse(...)).

Variables (fields?) Won't Display in Django Generic Detail View

I have been loosely following a tutorial and can't seam to get a generic detail view to work properly. I am calling with a pk and the page displays but the variable {{publisher.name}} doesn't show up. I have deleted some of the code from views and the model which I consider peripheral but if there error isnt obvious I can repost.
All files are in the poll directory except the HTML file is in poll/template/poll
Thanks
The URL.py is
from django.conf.urls import url
from poll.views import PublisherList
from . import views
app_name = "poll"
urlpatterns = [
url(r'^publishers/$', PublisherList.as_view(), name = "publisherlist"),
url(r'^start/', views.PublisherCreate.as_view(), name = 'make-publisher'),
url(r'^(?P<pk>[0-9]+)/$', views.PublisherDetail.as_view(), name = 'detail-publisher'),
]
The View.py
from django.shortcuts import render
from django.views.generic.edit import CreateView
from django.views import generic
from django.views.generic import ListView
from poll.models import Publisher
...
class PublisherDetail(generic.DetailView):
model = Publisher
template_name = 'Poll/publisher_details.html'
and the HTML file
{% extends "personal/header.html" %}
{% block content %}
<h1>{{ Publisher.name }}</h1>
<h1>Options</h1>
{%endblock%}
and the models.py
from django.db import models
from django.core.urlresolvers import reverse
# Create your models here.
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=50)
city = models.CharField(max_length=60)
state_province = models.CharField(max_length=30)
country = models.CharField(max_length=50)
website = models.URLField()
class Meta:
ordering = ["-name"]
def __str__(self): # __unicode__ on Python 2
return self.name
def get_absolute_url(self):
return reverse('build:details', kwargs = {'pk':self.pk})
The object in the template is called publisher, not Publisher.
You can't access the instance of the model like this in the template. Publisher is the name of your Model class but not an instance of it. The default name of the object in a generic detail view is object. So you need to use {{ object.name }}. Or you can use the lowercased Model name too as a default. In your case thats publisher.
If you want to change the variable name of your object you have to implement get_context_object_name(obj) method of your detail view.
The method has to return a string with the desired variable name of the object in your detail view template.
The default name of the object in the template is object, while the lowercase model name serves as an alias (publisher). You can specify that name in the view via the class attribute context_object_name, as described in the docs:
class PublisherDetail(generic.DetailView):
# ...
context_object_name = 'foo'
Then
{{ foo.name }}
# {{ publisher.name }} should work out of the box
will work in the template.

Error Instantiating View Classes in Django

I'm new to Django and have used generic views for all my projects so far. I've called them in the url.py code with
patterns(r'^url$',DetailView.as_view())
However, now I'm trying to make my own class based views and am having trouble at it. I tried making a simple test where I call a model.py function from my view, but I get an error that I haven't instantiated an instance of the class. How should I instantiate the view class? Also, how come I don't get the same error when calling DetailView.as_view() the DetailView class isn't instantiated either, right?
My code:
models.py
class Post(models.Model):
title = models.CharField(max_length=140)
body = models.TextField()
def __unicode__(self):
return self.title
def getBody(self):
return self.body
view.py
class RatingView(generic.DetailView):
model = Post
template_name = "like.html"
def __init__(self):
model = Post
template_name = "like.html"
def modelFuncTest(self):
return HttpResponse(self.model.getBody())
url.py
from django.conf.urls import patterns, include, url
from django.views.generic import ListView, DetailView
from blog.views import RatingView
from blog.models import Post
urlpatterns = patterns('',
url(r'^(?P<pk>\d+)/like/$', RatingView.modelFuncTest()),
)
Change your views.py to be just:
class RatingView(generic.DetailView):
model = Post
template_name = "like.html"
and change urls.py to be:
from django.conf.urls import patterns, url
from blog.views import RatingView
urlpatterns = patterns('',
url(r'^(?P<pk>\d+)/like/$', RatingView.as_view()),
)
and that should get you started, let me know if there are errors after making these changes.
There are some good examples in the django docs if you haven't seen them yet.
EDIT: Also, in the template, you should get your body like this:
{{ post.body }}
The beauty of the Class-based views is that the context of your object gets passed for you.
You still need to call the as_view() function on your class in your urlpatterns:
urlpatterns = patterns('',
url(r'^(?P<pk>\d+)/like/$', RatingView.as_view()),
)
As for this:
Also, how come I don't get the same error when calling DetailView.as_view() the DetailView class isn't instantiated either, right?
The as_view() function is a class method, which returns an instance of the view class that can be called when your URL pattern is hit.

what is Django formsets used for?

I found something strange in Django's documentation which is called formset, see it here.
I am wondering What is formset used for and How to use it?
Formset is an example of datagrid .
If you want to use multiple form of same type at one page.you can use Formset.
Example :
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ["username", "email"]
Now if you want to use UserForm multiple times at one page you need to use Formset.
from django.forms.formsets import formset_factory
Uforms = formset_factory(UserForm, extra = 4) #extra used to define how many empty forms will display
Into Your views.py
def submit(request):
if request.POST:
#code to manage post request
# validation to formset you can follow django docs
else:
address_formSet = Uforms(instance=UserForm())
# render response
Template code
{{ address_formset.as_table }}
You can follow step by step django formset to learn.
Example Code
class Address(models.Model):
city = models.CharField(max_length=48)
zipcode = models.IntegerField(max_length=5)
class Friend(models.Model):
name = models.CharField(max_length=30)
address = models.ForeignKey(Address)
forms.py
from django import forms
from .models import Address, Friend
from django.forms.models import inlineformset_factory
MAX_ADDRESS = 2 #updated
AddressFormSet = inlineformset_factory(Address, Friend, extra=MAX_ADDRESS) #updated
class UserAddressForm(forms.ModelForm):
class Meta:
model = Address
view.py
from django.shortcuts import render_to_response
from .models import *
from .forms import UserSubmittedAddressForm, AddressFormSet
def submit(request):
if request.POST:
#Logic
else:
form = UserAddressForm()
address_formSet = AddressFormSet(instance=Address())
# render response
template code
{{ form.as_table }}
{{ address_formset.as_table }}
It's used to work with e.g. a table of records. It's a way to create data grid functionality in such a way that Django does all the heavy lifting (all data for all the records are sent back in the same POST).

Categories

Resources