Getting context of parent in Foreign Key - Django - django

This is supposed to be really simple and it is in the documents. I am trying to get the context of a ListView. For example:
"We can also add the publisher into the context at the same time, so we can use it in the template http://docs.djangoproject.com/en/1.3/topics/class-based-views/#dynamic-filtering/:
class IssuesByTitleView(ListView):
context_object_name = "issue_list"
def get_queryset(self):
self.title = get_object_or_404(Title, slug=self.kwargs['title_slug'])
return Issue.objects.filter(title=self.title).order_by('-number')
My models.py look something like this:
class Title(models.Model):
CATEGORY_CHOICES = (
('Ongoing', 'Ongoing'),
('Ongoing - Canceled', 'Ongoing - Canceled'),
('Limited Series', 'Limited Series'),
('One-shot', 'One-shot'),
('Other', 'Other'),
)
title = models.CharField(max_length=64)
vol = models.IntegerField(blank=True, null=True, max_length=3)
year = models.CharField(blank=True, null=True, max_length=20, help_text="Ex) 1980 - present, 1980 - 1989.")
category = models.CharField(max_length=30, choices=CATEGORY_CHOICES)
is_current = models.BooleanField(help_text="Check if the title is being published where Emma makes regular appearances.")
slug = models.SlugField()
class Meta:
ordering = ['title']
def get_absolute_url(self):
return "/titles/%s" % self.slug
def __unicode__(self):
return self.title
class Issue(models.Model):
CATEGORY_CHOICES = (
('Major', 'Major'),
('Minor', 'Minor'),
('Cameo', 'Cameo'),
('Other', 'Other'),
)
title = models.ForeignKey(Title)
number = models.IntegerField(help_text="Do not include the '#'.")
.........
Views.py:
class IssuesByTitleView(ListView):
context_object_name = "issue_list"
def get_queryset(self):
self.title = get_object_or_404(Title, slug=self.kwargs['title_slug'])
return Issue.objects.filter(title=self.title).order_by('-number')
def get_context_data(self):
context = super(IssuesByTitleView, self).get_context_data()
context['title'] = self.title
return context
In my list of issues within the Title, I need to return the Title and all the other properties of the Title. That up there does not work and it returns an error:
get_context_data() got an unexpected keyword argument 'object_list'

You should use **kwargs in get_context_data
def get_context_data(self, **kwargs):
context = super(IssuesByTitleView, self).get_context_data(**kwargs)
context['title'] = self.title
return context

Related

Add a dynamic initial value in Filter

I want to add a dynamic initial value for django-filter or django-autocomplete-light to my DetailView. I don’t know how best to build it with django-filter, django-autocomplete-light or without third app.
I have a dependency dropdown (in my case, this is journal → journal year → journal volume) for each JournalDetailView. Dependency dropdown works with django-autocomplete-light and filter works with django-filter.
I want to pass dynamic field for journal so I have ForeignKey which is used for depends dropdown for journal year and journal volume
For example, In this case I have three fields: journal, journal year and journal volume. I want to pass value depends on DetailView for journal. For instance, for journal “Nature” it will pass field “Nature”; for journal “Ca-A Cancer Journal for Clinicians” it will pass “Ca-A Cancer Journal for Clinicians”.
I have this
I want to build this
models.py
class Journal(models.Model):
slug = models.SlugField(unique=True)
name = models.CharField(max_length=100, blank=True, null=True)
class Article(models.Model, HitCountMixin):
slug = models.SlugField(unique=True)
journal = models.ForeignKey(
"Journal", on_delete=models.SET_NULL, null=True, blank=True
)
journalyear = models.ForeignKey(
"JournalYear", on_delete=models.SET_NULL, null=True, blank=True
)
journalvolume = models.ForeignKey(
"JournalVolume", on_delete=models.SET_NULL, null=True, blank=True
)
def __str__(self):
return self.title
class JournalYear(models.Model):
journal = models.ForeignKey(
"Journal", on_delete=models.SET_NULL, null=True, blank=True
)
name = models.CharField(max_length=10, blank=True, null=True)
def __str__(self):
return self.name
class JournalVolume(models.Model):
journalyear = models.ForeignKey(
"JournalYear", on_delete=models.CASCADE, blank=True, null=True
)
name = models.CharField(max_length=10, blank=True, null=True)
def __str__(self):
return self.name
views.py
class JournalAutocomplete2(autocomplete.Select2QuerySetView):
def get_queryset(self):
# Don't forget to filter out results depending on the visitor !
if not self.request.user.is_authenticated:
return Journal.objects.none()
qs = Journal.objects.all()
if self.q:
qs = qs.filter(name__icontains=self.q)
return qs
class JournalYearAutocomplete2(autocomplete.Select2QuerySetView):
def get_queryset(self):
# Don't forget to filter out results depending on the visitor !
if not self.request.user.is_authenticated:
return JournalYear.objects.none()
journal = self.forwarded.get("journal")
qs = JournalYear.objects.all()
if journal:
qs = qs.filter(journal=journal)
if self.q:
qs = qs.filter(name__icontains=self.q)
return qs
class JournalVolumeAutocomplete2(autocomplete.Select2QuerySetView):
def get_queryset(self):
# Don't forget to filter out results depending on the visitor !
if not self.request.user.is_authenticated:
return JournalVolume.objects.none()
qs = JournalVolume.objects.all()
journalyear = self.forwarded.get("journalyear", None)
if journalyear:
qs = qs.filter(journalyear=journalyear)
if self.q:
qs = qs.filter(name__icontains=self.q)
return qs
class JournalListView(ListView):
model = Journal
template_name = "journals/journals.html"
context_object_name = "journals"
class JournalDetailView(DetailView):
model = Journal
template_name = "journals/journal_detail.html"
context_object_name = "journal"
def get_context_data(self, **kwargs):
context_data = super(JournalDetailView, self).get_context_data()
journal_slug = self.kwargs.get("slug", None)
f = JournalFilter(
self.request.GET,
queryset=Article.objects.filter(
journal__slug__exact=self.kwargs["slug"]),
)
context_data["filter"] = f
return context_data
filters.py
class JournalFilter(django_filters.FilterSet):
journal = django_filters.ModelChoiceFilter(
queryset=Journal.objects.all(),
widget=autocomplete.ModelSelect2(url="journalautocomplete2"),
)
journalyear = django_filters.ModelChoiceFilter(
queryset=JournalYear.objects.all(),
widget=autocomplete.ModelSelect2(
url="journalyearautocomplete2", forward=["journal"]
),
)
journalvolume = django_filters.ModelChoiceFilter(
queryset=JournalVolume.objects.all(),
widget=autocomplete.ModelSelect2(
url="journalvolumeautocomplete2", forward=["journalyear"]
),
)
class Meta:
model = Article
fields = {"journalyear", "journalvolume", "journal"}
urls.py
urlpatterns = [
path("journals/", JournalListView.as_view(), name="journals"),
path("journal/<str:slug>", JournalDetailView.as_view(), name="journal_detail"),
# Django-autocomplete-light
path(
"journalyearautocomplete2/",
JournalYearAutocomplete2.as_view(),
name="journalyearautocomplete2",
),
path(
"journalvolumeautocomplete2/",
JournalVolumeAutocomplete2.as_view(),
name="journalvolumeautocomplete2",
),
path(
"journalautocomplete2/",
JournalAutocomplete2.as_view(),
name="journalautocomplete2",
),
]
Edit:
It works approach for readers.
def article_list(request, slug):
journal = get_object_or_404(Journal, slug=slug)
my_filter_defaults = {'journal': journal}
f = JournalFilter(request.GET or my_filter_defaults)
return render(request, 'journals/journaldetail_list.html', {'filter': f, 'journal': journal})
Django-filter binds a dict, usually request.GET, to filter its queryset. From the doc:
def product_list(request):
f = ProductFilter(request.GET, queryset=Product.objects.all())
return render(request, 'my_app/template.html', {'filter': f})
So if you want defaults if no filtering has been specified, you could do
my_filter_defaults = { ... }
f = ProductFilter(request.GET or my_filter_defaults, queryset=Product.objects.all())
(Pythonic usage: an empty dict is Falsy so the the defaults get used instead)
You could also tinker with request.GET but be aware that it's a QueryDict and immutable, so you would have to copy it first
my_dict = request.GET.copy( )
if not my_dict.get( 'foo', None):
my_dict['foo'] = 'my_foo_default'
...
f = ProductFilter(my_dict, queryset=Product.objects.all())
I'm not familiar with DAL.

How can I create a related object in Django 2.0+, simply and effectively?

I'd like to find a simple and robust way to create a child object. I think it is a simple problem, probably solved using Django RelationshipManager or Related objects reference.
I've gotten it to work in the past (by paying someone on fiver to help me solve this), but I feel that there is a much simpler method that escapes me.
This worked on my views.py
class MainVisitForm(SingleObjectMixin, FormView):
template_name = "clincher/visit_form.html"
form_class = VisitForm
model = Main
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
form=self.get_form()
form.fk_visit_user = self.request.user
form.fk_visit_main = Main.objects.get(id=self.kwargs['pk'])
#added this to save form as we are mixxing the two forms and Models
# as the approch of singleObjectMixin is we should get object from DB as per request url as a primary key
#and we have defined model as a Main but taking the form view of VistForm as the probem occures
# as i think
if form.is_valid():
instance = Main()
instance.firstname = form.cleaned_data['firstname']
instance.middelname = form.cleaned_data['middlename']
instance.lastname = form.cleaned_data['lastname']
instance.date_of_birth = form.cleaned_data['date_of_birth']
instance.sex = form.cleaned_data['sex']
instance.address = form.cleaned_data['address']
instance.save()
return super().post(request, *args, **kwargs)
def get_success_url(self):
return reverse('clincher:main_detail', kwargs={'pk': self.object.pk})
Basically, while the user is in the details page of the "main" object, I would like them to be able to create a child object (visit object). Ultimately 1 patient will have many visits (1:m relationship). Each time a patient visits the doc, 1 new visit will be added, that is related to that person. Later, I will show a list of visits for that patient (but not the subject of this question).
Models.py
class Main(models.Model):
firstname = models.CharField(max_length = 256, verbose_name=('First Name'))
middlename = models.CharField(max_length=256, verbose_name=('Middle Name'))
lastname = models.CharField(max_length=256, verbose_name=('Last Name'))
date_of_birth = models.DateField()
age = models.CharField(max_length=4)
sex_list = (
(str(1), 'Female'),
(str(2), 'Male'),
(str(3), 'Other'),
(str(4), 'Unknown'),)
sex = models.CharField(max_length = 24, choices=sex_list, verbose_name='Sex')
address = models.TextField(max_length = 256)
#property
def full_name(self):
#"Returns the person's full name."
return '%s %s' % (self.firstname, self.lastname)
#Redirects after form is submitted using primary key
def get_absolute_url(self):
return reverse('clincher:main_detail', kwargs={'pk': self.pk})
def __str__(self):
return self.firstname + ' ' + self.lastname +' - ' + str(self.date_of_birth)
class Visit(models.Model):
fk_visit_main = models.ForeignKey(Main, on_delete=models.CASCADE, verbose_name=('Patient Name'))
visit_date = models.DateField(auto_now = True, editable=True)
fk_visit_user = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name=('Practitioner'), max_length=500)
visit_label = models.CharField(max_length=256, blank=True, null=True)
visit_type = models.CharField(
max_length=256,
default=1, verbose_name='Type of Visit')
visit_progress_notes = models.TextField(max_length=10000,
blank=True, verbose_name='Progress Notes')
outcomes = models.BooleanField(default=False)
def __str__(self):
return '%s %s' % (self.visit_date, self.visit_label)
def get_absolute_url(self):
return reverse('clincher:main_detail', kwargs={'pk': self.pk})
forms.py
class VisitForm(forms.Form):
visit_types_list = (
(str(1), 'Consultation'),
(str(2), 'Procedure'),
(str(3), 'Administrative'),)
visit_type = forms.ChoiceField(choices=visit_types_list)
visit_label = forms.CharField(label='Visit Label', max_length=100)
progress_note = forms.CharField(widget=forms.Textarea)
def form_valid(self, form):
form.instance.fk_visit_user = self.request.user
form.instance.fk_visit_main = Main.objects.get(id=self.kwargs['pk'])
return super().form_valid(form)
Thus, I should end up with a child record/object that has the primary key of the parent object.
The above code works, but I am sure that there is a simple Django-ey way of doing things much simpler, and in a robust manner. I think my solution should be found in the Django RelationshipManager, but I cannot find a solution that works. I paid a guy on Fiver, and I think he didn'y get this as simple as possible.
Check django InlineFormset: https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#inline-formsets
If you want to have to abillity to add an remove the formset dynamically checkout (Jquery based) :
https://github.com/elo80ka/django-dynamic-formset
If you are using class based view you will have to add the inlineformset in get_context_data() and inside form_valid() check if the formset.is_valid() and then save it to the database.
EDIT: Here is the code based on your comment
forms.py
class VisitForm(forms.ModelForm);
class Meta:
model = Visit
fields = [
'visit_type',
'visit_label',
'visit_progress_notes'
]
views.py
class CreateVisitView(CreateView):
model = Visit
form_class = VisitForm
template_name = "clincher/visit_form.html"
#one of the first function called in class based view, best place to manage conditional access
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
return super(CreateVisitView,self).dispatch(request, *args, **kwargs)
def form_valid(self, form):
visit = form.save(commit=False)
visit.fk_visit_user = self.request.username
visit.fk_visit_main = get_object_or_404(Main, pk=self.kwargs.get('pk'))
visit.save()
return super(CreateVisitView,self).form_valid(form)
models.py
class Main(models.Model):
SEX_LIST_CHOICE = (
(str(1), 'Female'),
(str(2), 'Male'),
(str(3), 'Other'),
(str(4), 'Unknown'),
)
firstname = models.CharField(max_length = 256, verbose_name=('First Name'))
middlename = models.CharField(max_length=256, verbose_name=('Middle Name'))
lastname = models.CharField(max_length=256, verbose_name=('Last Name'))
date_of_birth = models.DateField()
age = models.PositiveSmallIntegerField()
sex = models.CharField(max_length = 24, choices=SEX_LIST_CHOICE, verbose_name='Sex')
address = models.TextField(max_length = 256)
#property
def full_name(self):
#"Returns the person's full name."
return '%s %s' % (self.firstname, self.lastname)
#Redirects after form is submitted using primary key
def get_absolute_url(self):
return reverse('clincher:main_detail', kwargs={'pk': self.pk})
def __str__(self):
return self.firstname + ' ' + self.lastname +' - ' + str(self.date_of_birth)
class Visit(models.Model):
VISIT_TYPE_CHOICE = (
(str(1), 'Consultation'),
(str(2), 'Procedure'),
(str(3), 'Administrative'),)
fk_visit_main = models.ForeignKey(Main, on_delete=models.CASCADE, verbose_name=('Patient Name'))
visit_date = models.DateField(auto_now = True, editable=True)
fk_visit_user = models.ForeignKey(User, on_delete=models.PROTECT, verbose_name=('Practitioner'), max_length=500)
visit_label = models.CharField(max_length=256, blank=True, null=True)
#you are storing the type of visit as an
visit_type = models.CharField(
max_length=256,
default=1,
verbose_name='Type of Visit',
choices=VISIT_TYPE_CHOICE
)
visit_progress_notes = models.TextField(max_length=10000,
blank=True, verbose_name='Progress Notes')
outcomes = models.BooleanField(default=False)
def __str__(self):
return '%s %s' % (self.visit_date, self.visit_label)
def get_absolute_url(self):
return reverse('clincher:main_detail', kwargs={'pk': self.pk})
So a number of things here that you could clear up.
instance.middelname = form.cleaned_data['middlename'] Will never work as middlename is incorrect on the instance side.
You can use Main.objects.create(firstname=form.validated_data['firstname'], lastname= .... etc) to create your Model instances
You should probably have the relation from a User to your models be via Main, not Visit. This will allow you to add records for a Visit easier, for example, staff member logging visits instead of customer.
You should lookup CreateView to assist you with the boilerplate of creating an instance.
Rename the Main model. What is it actually? Looks like a Profile to me, but calling it Main isn't very descriptive.
Age should be an integer field. Nobody is 'dave' years old.

showing entries based on selection type in django

my question is i want to show only particular titles under music_track (musicmodel)field when type = track(title model) in my django admin site
class album(models.Model):
def get_autogenerated_code():
last_id = album.objects.values('id').order_by('id').last()
if not last_id:
return "AL-"+str(0)
return "AL-"+str(last_id['id'])
album_name = models.CharField( max_length=150, blank=False )
music_track = models.ManyToManyField("title")
def __str__(self):
return (self.album_name)
class Meta:
verbose_name = "Album"
verbose_name_plural = "Albums"
class title(models.Model):
def get_autogenerated_code():
last_id = title.objects.values('id').order_by('id').last()
if not last_id:
return "TT-"+str(0)
return "TT-"+str(last_id['id'])
upc_code = models.CharField(max_length=15, default="N/A", blank=False)
display_name = models.CharField(max_length=150, blank=False)
type = models.ForeignKey(Asset_Type, on_delete=models.CASCADE, null=True)
def __str__(self):
return (self.display_name+ " " +self.code)
admin.site.register( [album, title] )
From your question, I am understanding that while creating an album in your admin panel, you require that the music_track should only show the titles having type as track. My solution for this is:
In your admin.py file
from .models import title, album, Asset_type
class AlbumForm(forms.ModelForm):
class Meta:
model = Product
fields = ('album_name', 'music_track', )
def __init__(self, user, *args, **kwargs):
super(AlbumForm, self).__init__(*args, **kwargs)
type = Asset_type.objects.get(type='track')
self.fields['music_track'].queryset = Title.objects.filter(type=type)
class MyModelAdmin(admin.ModelAdmin):
form = AlbumForm
admin.site.register(album, MyModelAdmin)
Maybe this can give you the idea you need.

Django problem with having slug in my url while using a class

First i was using a detailslist with a def which was working fine, but i wanted to add a widget to show similar post (post in same category) so i had to change my def to a class to put a def get_context_data(self, pk), but now it always say et_context_data() got an unexpected keyword argument 'object'
here is my url
url(r'^(?P<slug>[\w-]+)$', views.postdetails.as_view(), name="postdetails"),
my view
class postdetails(DetailView):
model = Post
template_name = 'blog/post.html'
def get_context_data(self,**kwargs):
context = super(postdetail, self).get_context_data(**kwargs)
cat_id = self.kwargs.get('pk', None)
category = get_object_or_404(Category, id=cat_id)
getcat = category.post_set.all().order_by("-date")
resultcat = random.sample(getcat,4)
context['similarpost'] = resultcat
return context
and my model if needed
class Post(models.Model):
title = models.CharField(max_length = 140, unique=True)
slug = models.SlugField(max_length=40, blank=True, unique=True)
image = models.ImageField(upload_to="media", blank=True)
body = RichTextField(config_name='default')
date = models.DateField()
category = models.ManyToManyField(Category)
def __str__(self):
return self.title
I changed the pk in def get_context_data(self,pk): to **kwargs
but i get name 'postdetail' is not defined
thanks a lot

How to surface two Django models on one page?

I would like to create one page that surfaces two separate Django models:
class Client(models.Model):
name = models.CharField(max_length=100)
slug = AutoSlugField(populate_from='name', blank=True, unique=True)
order = models.IntegerField(editable=False, default=0)
class Meta:
ordering = ('order',)
def __unicode__(self):
return self.name
class Press(models.Model):
title = models.CharField(max_length=50)
article = models.ImageField(upload_to = 'images')
def image_thumb(self):
if self.article:
return u'<img src="%s" height="125"/>' %self.article.url
else:
return "no image"
image_thumb.short_description = "article"
image_thumb.allow_tags = True
class Meta:
verbose_name_plural = "press"
I am unsure how to write my queryset in Views.py. I've tried something like this...
class ClientView(generic.ListView):
template_name = 'clients.html'
context_object_name = 'client'
def queryset(request):
client_page = {'press': Press.objects.all(), 'client': Clients.objects.all()}
return client_page
and then this in my urls.py...
url(r'^clients/', views.ClientView.as_view(), name = 'client_model'),
I read in a stack answer that I can do this by using "get_extra_context" but can someone show me how that's used?
class ClientView(generic.ListView):
# ...
def get_context_data(self, **kwargs):
context = super(ClientView, self).get_context_data(**kwargs)
context['press'] = Press.objects.all()
return context