Django - Class Based View for a specific item - django

Building a Django app where I have a view that displays a list of distinct case. When you click on a case, I'd like it to take you to a list of items related to the case (in this case it's a list of devices). The issue I am facing I don't know how to make the view display only the items related to that case (right now it displays every item in every case).
Views:
class MdeListView(LoginRequiredMixin, ListView):
model = Mde
template_name = 'mde/mde.html'
ordering = [F('date').desc()]
def get_queryset(self):
return Mde.objects.distinct('case_number')
class MdeCaseListView(LoginRequiredMixin, ListView):
model = Mde
template_name = 'mde/mde_case_list.html'
urls.py
from django.urls import path
from .views import MdeListView, MdeCreateView, MdeCaseListView
urlpatterns = [
path('<int:pk>/list', MdeCaseListView.as_view(), name='mde_case_list'),
path('new', MdeCreateView.as_view(), name='mde_new'),
path('', MdeListView.as_view(), name='mde'),
]
The url goes to the right record based on the primary key, but from there I want only the items that use the same case_number as that primary key.

I was able to figure it out!
class MdeCaseListView(LoginRequiredMixin, ListView):
model = Mde
template_name = 'mde/mde_case_list.html'
def get_queryset(self):
pkey = self.kwargs.get("pk")
case = Mde.objects.filter(id=pkey).values_list('case_number', flat=True)
return Mde.objects.filter(case_number=case[0])

Related

Trouble Inheriting a function that renders data in a class containg a django generic view

First, I have model (Entry), where one field, 'prompt', is where I want to store text, "You suck bro!", in my django admin database. The second field, 'body', is a text editor I want to show on my html page.
enter image description here
In my views file, I have a function: def indexx(request), which renders text from the admin database, field 'prompt' on to my html page.
views.py
def indexx(request):
entry = Entry.objects.all()
return render(request, "entries/entries.html", {"ents" : entry})
urls.py
from django.urls import path
from .views import indexx
urlpatterns = [
path("", indexx)
]
This only renders text from django models: field ('prompt').
enter image description here
Next, in my views file, I have a class, class EditEntryView(CreateView), where "(CreateView)", from "django.views.generic import CreateView". This allows me to render the text editor on my html page. The vies and urls are under the same python files as well.
views.py
class EditEntryView(CreateView):
model = Entry
# form_class = PostEntry
fields = ["body" ]
template_name = "entries/entries.html"
urls.py
from django.urls import path
from .views import EditEntryView
urlpatterns = [
path("", EditEntryView.as_view(), name="entries")
]
This class renders the text editor.
enter image description here
My goal is to render the two separate get objects, (def index(request), EditEntryView(CreeateView) on to the same html page. I want to be able to loop through data in my database while the text editor remains static on the html page. The text, "You suck bro!" should be placed right above the text editor.
enter image description here
Any help would be helpful!
I have tried putting my def indexx(request) function, inside of the class EditEnrtyView(CreateView). That does not seem to work.
class EditEntryView(CreateView):
model = Entry
# form_class = PostEntry
fields = ["body" ]
template_name = "entries/entries.html"
def indexx(request):
entry = Entry.objects.all()
return render(request, "entries/entries.html", {"ents" : entry})
With my url pathing as:
path("", EditEntryView.as_view(), names="entries")
However, the only object that is rendered is the text editor:
You can't do it like this.
What indexx does is render the template entries/entries.html with the context {"ents" : entry} and return that as an HttpResponse object.
It's basically a ListView.
I'm not sure from the description what you want to accomplish. A CreateView is creating a new object. If you want to display all objects of that type including the new one after it is created, you would add to the CreateView a get_success_url method to redirect to the list view:
def get_success_url(self):
return reverse( 'app:indexx_url_name')
or since this doesn't have any args or kwaygs it can be simplified to
success_url = reverse_lazy( 'app:indexx_url_name')
(If you use plain reverse, you are likely to get horrible circular import errors, quite possible later when you edit something with no obvious connection to this cause)
The other thing you might do if you want a display of a list of objects which do exist while creating the new one, is to render the list in the template of the create view. You'll need to pass the list of objects as ents to the create view's context:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['ents'] = Entry.objects.all()
return context
and then you can do the same in the CreateView as you did in indexx's template using Django template language.

Can you pass an argument to ListView in Django?

I am creating a Fixture webapp with Django. I have written the class below, which displays a list of first team fixtures. Can I rewrite it as TeamView, then pass the team_id?
class FirstView(generic.ListView):
template_name = 'fixtureapp/team.html'
context_object_name = 'fixtures'
def get_queryset(self):
"""return all first team matches"""
return Match.objects.filter(team_id=1).order_by('date')
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
data['page_title'] = '1st Team Fixtures'
return data
I have the following urls, how would I rewrite these to match?
urlpatterns = [
path('', views.HomeView.as_view(), name='home'),
path('first', views.FirstView.as_view(), name='first'),
path('second', views.SecondView.as_view(), name='second'),
as you can see I have currently created a second class called SecondView this is almost a carbon copy of FirstView, not very DRY
I can give you a brief of how it works, you can apply rest of the logic. Basic idea is to use slug.
In your html you can give the slug name with the url:
In urls.py, get that slug :
path('team/<slug:teamid_slug>/', views.TeamView.as_view(), name='team_by_id'),
Your Views should filter the query based on this slug, if no slug given it will give all the Match record. You can apply some other logic what fits to you.
class TeamView(generic.ListView):
queryset = Match.objects.all().order_by('date')
template_name = 'fixtureapp/team.html'
context_object_name = 'fixtures'
def get_queryset(self):
"""return all team_id team matches"""
return Match.objects.filter(team_id__slug=self.kwargs.get('teamid_slug')).order_by('date')
Also please have a look at this documentation of Dynamic Filtering

How to add pagination to FilterView

I've filtering view of django_filters.FilterSet which is called right from urls.py
url(r'^$', FilterView.as_view(filterset_class=ProductFilter, template_name='products/products.html'), name='products'),
and it's has no pagination, but when i add paginate_by = 20 in
url(r'^$', FilterView.as_view(filterset_class=ProductFilter, template_name='products/products.html'), paginate_by = 20, name='products'),
it adds my custom pagination page, but it's not handling data restricted by filters. So i can apply a few filters and it reduces data to, say 40 rows, but clicking on a second page it loads my all data without any filter. Could I specify that I want to paginate data after filtering somehow?
At the end I decided to create separate view and add queryset directly to context object like:
class ProductView(ListView):
model = Product
template_name = 'products/products.html'
paginate_by = 5
def get_context_data(self, **kwargs):
context = super().get_context_data()
context['product_list'] = ProductFilter(self.request.GET, queryset=Product.objects.order_by('id')).qs
return context
I found this to be much simpler to achieve pagination with filters on a given view:
class ProductView(ListView):
model = Product
template_name = 'products/products.html'
paginate_by = 5
context_object_name = 'products'
def get_queryset(self):
return Product.objects.filter(my_field=my_criteria)

Passing more than one view to template

I have a page that should show an event and the presentations of this event. In my code they are not related (I still have to fix that). On the home page, which receives the event and the lectures, the view is like this:
views.py
class EventoView(ListView):
model = Evento
template_name = 'home.html'
context_object_name = 'evento_list'
queryset = Evento.objects.all().order_by('-data')[:1]
class RegistroView(ListView):
model = Registro
template_name = 'home.html'
context_object_name = 'registro_list'
queryset = Registro.objects.all()
The problem is that I can only pass the Event object, the object of Registration, which show lectures Indexed must also be passed, however, only accepts a Django view for url.
urls.py
urlpatterns = patterns('',
url(r'^$', EventoView.as_view(), name='home'), #I can't pass two views
url(r'^cadastro/', CriarRegistroView.as_view(), name='cadastro'),
url(r'^contato/', CriarContatoView.as_view(), name='contato'),
url(r'^sobre/', SobreView.as_view(), name='sobre'),
url(r'^admin/', include(admin.site.urls)),
)
How can I solve this problem?
Thanks.
it looks like you can override ListView.get_context_data
class RegistroView(ListView):
model = Evento
def get_context_data(self, **kwargs):
context = super(RegistroListView, self).get_context_data(**kwargs)
context['registros'] = Registro.objects.all()
context['eventos'] = Evento.objects.all().order_by('-data')[:1]
return context
I don't have experience with ListViews so i don't know if i am using it as it is supposed to be used, or not

django dynamic form generation

say I have a model such:
class ComponentLength(models.Model):
component_name = models.CharField(max_length=155)
length1 = models.IntegerField()
length2 = models.IntegerField()
length3 = models.IntegerField()
length4 = models.IntegerField()
Now I have a form for the user to select a component, and on the next page I want to display 4 checkboxes for the various length options, which are different for different components.
What is the best way in Django to generate the form with these checkboxes based on the component name (accessible in session data) already selected by the user.
Any help much appreciated.
You could use a normal django form and change its fields upon instantiation, in the init method. Something like this:
class SecondForm(forms.Form):
def __init__(self, *args, **kwargs):
super(SecondForm, self).__init__(*args, **kwargs)
try:
#get the object, something like this:
obj_ = ComponentLength.objects.get(component_name = session.get('component_name_or_whatever_you_stored'))
except:
#handle the error case, e.g:
return
self.fields['length1'] = forms.CheckboxInput(attrs={'value' : obj_.length1 })
self.fields['length2'] = forms.CheckboxInput(attrs={'value' : obj_.length2 })
self.fields['length3'] = forms.CheckboxInput(attrs={'value' : obj_.length3 })
self.fields['length4'] = forms.CheckboxInput(attrs={'value' : obj_.length4 })
#Consider using a hidden input instead of polluting the session variables
#with form data
self.fields['component_length'] = forms.HiddenInput(attrs={'value' : obj_.pk})
The above code is not tested, but I expect it should work. Please let me know how it goes.
Form wizards is exactly what you need.
https://docs.djangoproject.com/en/1.7/ref/contrib/formtools/form-wizard/
See the example shown here https://docs.djangoproject.com/en/1.7/ref/contrib/formtools/form-wizard/#usage-of-namedurlwizardview
And how forms are defined here https://docs.djangoproject.com/en/1.7/ref/contrib/formtools/form-wizard/#conditionally-view-skip-specific-steps
I haven't tested the code below, but it should be something similar to the following:
myapp/forms.py
from django.forms import ModelForm
from myapp.models import ComponentLength
class ComponentLengthNameForm(ModelForm):
class Meta:
model = ComponentLength
fields = ['component_name',]
class ComponentLengthChoicesForm(ModelForm):
class Meta:
model = ComponentLength
fields = ['length1', 'length2', 'length3', 'length4',]
myapp/views.py
from django.contrib.formtools.wizard.views import SessionWizardView
from django.shortcuts import render_to_response
class ComponentWizard(SessionWizardView):
def done(self, form_list, **kwargs):
return render_to_response('done.html', {
'form_data': [form.cleaned_data for form in form_list],
})
myapp/urls.py
from django.conf.urls import url, patterns
from myapp.forms import ComponentLengthNameForm, ComponentLengthChoicesForm
from myapp.views import ContactWizard
named_contact_forms = (
('name', ComponentLengthNameForm),
('length-choices', ComponentLengthChoicesForm),
)
component_wizard = ComponentWizard.as_view(named_contact_forms,
url_name='component-wizard-form-step', done_step_name='finished')
urlpatterns = patterns('',
url(r'^my-form/(?P<step>.+)/$', component_wizard, name='component-wizard-form-step'),
url(r'^my-form/$', component_wizard, name='component-wizard-form'),
)