ListView unrelated model lookup - django

I'm looking for a way to bring data from one model into a view of another model for a ListView. Basically, when the user opens up the listview of the vocabulary model, I need to look up and pull out the associated pinyin field from the dictionary model by using the vocab field. There is some example code at the bottom of the codeview that would be how I would match up a Vocabulary.vocab with a Dictionary.pinyin.
############### Models
​
class Vocabulary(models.Model):
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
vocab = models.CharField(max_length=255, blank=False, null=False)
translation = models.CharField(max_length=255, blank=False, null=False)
level = models.PositiveSmallIntegerField(blank=True, null=True, choices=LEVELS, default=0)
audio = models.URLField(blank=True, null=True)
objects = RandomManager()
​
class Meta:
constraints = [
models.UniqueConstraint(fields=['created_by', 'vocab'], name='User Vocab Duplicate Check')
]
verbose_name = "Chinese Vocabulary"
verbose_name_plural = verbose_name
​
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.vocab
​
class Dictionary(models.Model):
traditional = models.CharField(max_length=20)
simplified = models.CharField(max_length=20)
pinyin = models.CharField(max_length=255)
simplified_radical = models.CharField(max_length=20)
hsk_level = models.PositiveSmallIntegerField(blank=True, null=True, choices=LEVELS, default=0)
frequency_rank = models.PositiveIntegerField(blank=True, null=True)
phrase_url = models.CharField(max_length=200)
radical_url = models.CharField(max_length=200)
definition = models.TextField()
objects = RandomManager()
​
class Meta:
verbose_name = "Dictionary"
verbose_name_plural = "Dictionary"
​
def __str__(self):
return self.simplified
​
################# Views
​
class VocabDetailView(LoginRequiredMixin, DetailView):
model = Vocabulary
template_name = 'library/vocab_details.html'
​
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['dictionary'] = Dictionary.objects.get(simplified=self.object.vocab)
return context
​
​
class VocabListView(LoginRequiredMixin, ListView):
model = Vocabulary
template_name = 'library/vocab_list.html'
paginate_by = 50
def get_queryset(self):
return Vocabulary.objects.filter(created_by=self.request.user)
​
'''
hao = Vocabulary.objects.get(vocab='你')
haodict = Dictionary.objects.get(simplified=hao.vocab)
haopin = hao_dict.pinyin
'''

If you objects were related, you could override the get_queryset to use select_related or similar in order to get the related objects. But it doesn't look like you can do that because the objects aren't related.
So you could either not use a ListView and gather your objects in the context, or override the context to get the Dictionary objects you want. Sounds like you'd do something like;
def get_context_data(**kwargs):
context = super().get_context_data(**kwargs)
dicts = Dictionary.objects.filter(pinyin__in=self.object_list.values_list('vocab', flat=True))
context['dictionaries'] = dicts
return context

Related

Autofilling Django model form field with data from associated objects

I have a model form that creates a new job entry, and on submission, I need an invisible field job_time_estimation to be set to a sum of 'service_stats_estimate_duration' values from ServiceItemStats objects associated with the JobEntry by a many-to-many relationship when submitting the form.
For example, if in my NewJobEntryForm I chose two existing ServiceItemStats objects that have service_stats_estimate_duration values 60 and 90, on submission, I want a value 150 to be saved in that JobEntry object's job_time_estimation attribute.
I tried doing this using aggregation by defining a save() method in the model but I am getting an error "name 'serviceItemStats' is not defined".
I am not sure if I am going about this the right way. Any help would be appreciated.
My code:
models.py:
class ServiceItemStats(models.Model):
service_stats_name = models.CharField(primary_key=True, max_length=20)
service_stats_estimate_duration = models.IntegerField()
# Many-to-many relationship with JobEntry.
def __str__(self):
return self.service_stats_name
class JobEntry(models.Model):
# PK: id - automatically assigned by Django.
jo
b_entry_date_time = models.DateTimeField(default=timezone.now)
jo
b_date = models.DateField(blank=True, null=True)
job_checked_in = models.BooleanField()
job_checked_out = models.BooleanField(default=False)
job_priority = models.IntegerField()
job_time_estimation = models.IntegerField(blank=True, null=True)
job_comments = models.TextField(max_length=200, blank=True, null=True)
job_parts_instock = models.BooleanField(default=False)
job_started = models.BooleanField(default=False)
job_finished = models.BooleanField(default=False)
job_expand_fault_evidence = models.ImageField(blank=True, null=True)
job_expand_comments = models.ImageField(blank=True, null=True)
job_expand_parts_required = models.CharField(max_length=200, blank=True, null=True)
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE) #One-to-one relationship
customer = models.ForeignKey(Customer, on_delete=models.CASCADE) #One-to-one relationship
serviceBay = models.ForeignKey(ServiceBay, on_delete=models.CASCADE, blank=True, null=True) #One-to-one relationship
serviceItemStats = models.ManyToManyField(ServiceItemStats, blank=True) #Many-to-many relationship
def __str__(self):
return self.id
def save(self, *args, **kwargs):
if not self.job_time_estimation:
self.job_time_estimation = serviceItemStats.objects.all().aggregate('service_stats_estimate_duration')
return super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse("jobs:job_detail",kwargs={'pk':self.pk})
views.py
class JobCreateView(FormView):
template_name = "jobs/jobentry_form.html"
form_class = NewJobEntryForm
success_url = reverse_lazy("jobs:job_list")
def form_valid(self, form):
form.save()
return super(job_list, self).form_valid(form)
forms.py
class NewJobEntryForm(ModelForm):
class Meta:
model = JobEntry
fields = ['vehicle', 'customer', 'job_date', 'job_checked_in', 'job_priority', 'job_comments', 'job_parts_instock', 'serviceItemStats']
widgets = {
'job_date' : forms.DateInput(format=('%m/%d/%Y'), attrs={'class':'form-control', 'placeholder':'Select a date', 'type':'date'}),
'ServiceItemStats' : forms.CheckboxSelectMultiple(),
'job_priority' : forms.RadioSelect(choices=priorityOptions),
}
You can try this.
from django.db.models import Sum
class JobCreateView(FormView):
template_name = "jobs/jobentry_form.html"
form_class = NewJobEntryForm
success_url = reverse_lazy("jobs:job_list")
def form_valid(self, form):
job=form.save()
estimation = job.serviceItemStats.all().aggregate(total=Sum('service_stats_estimate_duration'))
job.job_time_estimation = estimation['total']
job.save()
return super(job_list, self).form_valid(form)

Django ModelChoiceField: filtering object based on pk in url

I've read many questions about this topic, but none of the methods work for me.
There are 3 related models:
class Trips(models.Model):
lake = models.CharField("Lake", max_length=150)
city = models.CharField("City", max_length=100, blank=True)
s_date = models.DateTimeField("Starting Date", auto_now=False, auto_now_add=False)
e_date = models.DateTimeField("Ending Date", auto_now=False, auto_now_add=False)
trip_id = models.AutoField(primary_key=True)
class Meta:
verbose_name = "Trip"
verbose_name_plural = "Trips"
def __str__(self):
return f"{self.lake}-{self.trip_id}-{self.s_date}"
class Fisherman(models.Model):
name = models.CharField("Fisherman", max_length=50)
trip = models.ForeignKey(Trips, on_delete=models.CASCADE)
fisherman_id = models.AutoField(primary_key=True)
class Meta:
verbose_name = "Fisherman"
verbose_name_plural = "Fishermen"
def __str__(self):
return f"{self.name}-{self.fisherman_id}"
class Catch(models.Model):
fish_type = models.CharField("Fish Type", max_length=50)
catch_id = models.AutoField(primary_key=True)
weight = models.DecimalField("Weight", max_digits=5, decimal_places=2)
length = models.DecimalField("Length", max_digits=5, decimal_places=2, blank=True, null=True)
datetime = models.DateTimeField("Catch Time", auto_now=False, auto_now_add=False)
fisherman = models.ForeignKey(Fisherman, on_delete=models.CASCADE)
trip = models.ForeignKey(Trips, on_delete=models.CASCADE)
class Meta:
verbose_name = "Catch"
verbose_name_plural = "Catches"
def __str__(self):
return f"{self.fish_type}-{self.catch_id}"
I have a ModelForm to create a new catch. Here I use a ModelChoiceField to list Fishermen, but I don't know how to filter them. I only want display those who belong to the trip.
class CatchForm(forms.ModelForm):
fisherman = forms.ModelChoiceField(queryset= Fisherman.objects.all())
class Meta:
model = Catch
fields = ["fish_type", "weight", "length", "datetime", "fisherman"]
widgets = {
"datetime": forms.DateTimeInput(format='%Y-%m-%d %H:%M', attrs={'class':'datetimefield form-control'}),
}
views.py
I' ve read that get_form_kwargs should be used in views to override fields in the form, but it didn't work for me.
class NewCatchView(CreateView):
model = Catch
form_class = CatchForm
template_name = "new_trip/new_catch.html"
# Probably, this is wrong
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['fisherman'] = Fisherman.objects.filter(trip=self.kwargs.get('pk'))
return kwargs
def form_valid(self, form):
form.instance.trip = Trips.objects.get(pk=self.kwargs['pk'])
return super().form_valid(form)
def get_success_url(self):
return reverse('new_trip:trip_details', args=(self.kwargs['pk'],))
urls.py
path("trip_details/<int:pk>/new_catch/", views.NewCatchView.as_view(), name="new_catch"),
Thank you in advance for your help!
You're almost there. You've created the kwarg, so now you just need to use it in the form to overwrite the original queryset:
class CatchForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
fisherman = kwargs.pop('fisherman')
super().__init__(*args, **kwargs)
self.fields['fisherman'].queryset = fisherman

How to check the name of a model in a Django Template

I'm trying to get the name of a model in my template so i can give it a different design in the template
#views.py
class PostListView(ListView):
model = Post
template_name = 'blog/home.html'
context_object_name = 'posts'
paginate_by = 15
def get_queryset(self):
posts = []
shared_post = []
if self.request.user.is_authenticated:
user_id = self.request.user.id
view_user_post = Post.objects.filter(user=self.request.user)
user_profile = User.objects.get(id=user_id).profile
# print(user_profile)
for profile in user_profile.follower.all():
for post in Post.objects.filter(user=profile.user):
posts.append(post)
for profile in user_profile.follower.all():
for share in Share.objects.filter(user=profile.user):
shared_post.append(share)
chain_qs = chain(posts, view_user_post, shared_post)
print(chain_qs)
return sorted(chain_qs, key=lambda x: x.date_posted, reverse=True)
else:
posts = Post.objects.all().order_by('?')
return posts
#models.py
class Share(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
content = models.TextField(max_length=140, null=True, blank=True)
date_posted = models.DateTimeField(default=timezone.now)
def __str__(self):
return '{}- {}'.format(self.post.title, str(self.user.username))
class Post(models.Model):
title = models.CharField(max_length=140)
content = models.TextField(validators=[validate_is_profane])
likes = models.ManyToManyField(User, related_name='likes', blank=True)
date_posted = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE)
image = models.ImageField(upload_to='post_pics', blank=True)
image_2 = models.ImageField(upload_to='post_pics', blank=True)
image_3 = models.ImageField(upload_to='post_pics', blank=True)
restrict_comment = models.BooleanField(default=False)
saved = models.ManyToManyField(User, related_name='saved_post', blank=True)
I need a way to check the name of the model in the template possibly an if/ else statement to check properly. thanks
What about create a function inside your model that will return the name of the model?
Inside your models.py for each model:
def get_my_model_name(self):
return self._meta.model_name
Inside your template then yo can do something like:
{%if post.get_my_model_name == 'post'%}
Do something ...
Instead of checking the model name I suggest you implement a boolean property in each model that returns True in one case and False in the other one. For example:
class Post(models.Model):
# whatever fields and methods
#property
def is_shared(self):
return False
class Share(models.Model):
# whatever fields and methods
#property
def is_shared(self):
return True
Then in your template just check {% if post.is_shared %}

How create trigger in Django Rest Framework to change booleanfield?

I have a question in django rest framework. Since I'm learning how to use some advanced options, I do not quite understand. I need to currently change a booleanfield every time a foreignkey is inserted into table.
How can I do this in model ?
Model:
class Persona(models.Model):
name = models.CharField(max_length=32)
cart = models.ForeignKey(Credit,null=True)
rg = models.IntergerField()
end = models.CharField(max_length=256)
details = models.TextField(blank=True, null=True)
order = models.ForeignKey(Order, null=True)
def __str__(self):
return self.cart
class Meta:
db_table='person'
app_label = 'bank'
class Credit(models.Model):
number = models.CharField(max_length=16, unique=True)
create_at = models.DateField(auto_add_now=True)
updated_at = models.DateField()
available = models.BooleanField()
def __str__(self):
return self.number
class Meta:
db_table = 'credit'
app_label = 'bank'
Serializer:
class PersonaSerializer(serializers.ModelSerializer):
order__id = serializers.ReadOnlyField(source='order.id')
class Meta:
model = Persona
fields = '__all__'
class Persona(viewsets.ModelViewSet):
allowed_methods = ('GET', 'POST', 'PUT', 'PATCH')
queryset = Persona.objects.all()
serializer_class = PersonaSerializer
You can override the create method on the ModelSerializer to achieve this:
def create(self, validated_data):
cart = validated_data['cart']
persona = super(PersonaSerializer, self).create(validated_data)
cart.available = True # update boolean
cart.save()
return persona
You can read more about this in the docs
If you want to handle this in your model you can override the Persona model save method:
class Persona(models.Model):
name = models.CharField(max_length=32)
cart = models.ForeignKey(Credit,null=True)
rg = models.IntergerField()
end = models.CharField(max_length=256)
details = models.TextField(blank=True, null=True)
order = models.ForeignKey(Order, null=True)
def __str__(self):
return self.cart
class Meta:
db_table='person'
app_label = 'bank'
def save(self, *args, **kwargs):
# Will only update the available field when the Persona
# instance is created and the cart field is not null
if not self.pk and self.cart:
self.cart.available = True
self.cart.save()
return super(Persona, self).save(*args, **kwargs)

Cannot assign must be a instance Django

I have an order form which returns this statement of submit:
Cannot assign "<Annual: 2012>": "Order.annuals" must be a "Catalog" instance.
I'm fairly new to Django. I understand it needs an instance instead of the string it has been passed. How would I go about resolving that?
Here is my view:
class OrderListCreateView(
views.LoginRequiredMixin,
views.SetHeadlineMixin,
generic.CreateView
):
form_class = forms.OrderListForm
headline = 'Create'
model = Order
template_name = 'ordercreate.html'
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super(OrderListCreateView, self).form_valid(form)
Here is my form:
class OrderListForm(forms.ModelForm):
annuals = forms.ModelChoiceField(queryset=Annual.objects.all())
issues = forms.ModelChoiceField(queryset=Issue.objects.all())
articles = forms.ModelChoiceField(queryset=Article.objects.all())
class Meta:
fields = (
'annuals',
'issues',
'articles',)
model = models.Order
def __init__(self, *args, **kwargs):
super(OrderListForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
'annuals',
'issues',
'articles',
ButtonHolder(
Submit('create', 'Create')
)
)
Here is my model:
class Catalog(models.Model):
products = models.CharField(max_length=200)
def __unicode__(self):
return self.products
class Issue(models.Model):
catalog = models.ForeignKey(Catalog, related_name='issue_products')
Volume = models.DecimalField(max_digits=3, decimal_places=1)
def __unicode__(self):
return unicode(self.Volume)
class Annual(models.Model):
catalog = models.ForeignKey(Catalog, related_name='annual_products')
year_id = models.IntegerField(max_length=4)
start_date = models.CharField(max_length=6)
end_date = models.CharField(max_length=6)
def __unicode__(self):
return unicode(self.year_id)
#def __unicode__(self):
# return unicode(self.id)
class Annual_Issue(models.Model):
annual_id = models.ForeignKey(Annual, related_name='annual_ids')
issue_id = models.ForeignKey(Issue, related_name='issues')
def __unicode__(self):
return self.annual_id
class Article(models.Model):
catalog = models.ForeignKey(Catalog, related_name='article_products')
title = models.CharField(max_length=200)
abstract = models.TextField(max_length=1000, blank=True)
full_text = models.TextField(blank=True)
proquest_link = models.CharField(max_length=200, blank=True, null=True)
ebsco_link = models.CharField(max_length=200, blank=True, null=True)
def __unicode__(self):
return self.title
class Order(models.Model):
user = models.ForeignKey(User, related_name='who_ordered')
annuals = models.ForeignKey(Catalog, related_name='annuals_ordered', blank=True, null=True)
issues = models.ForeignKey(Catalog, related_name='issues_ordered', blank=True, null=True)
articles = models.ForeignKey(Catalog, related_name='items_ordered', blank=True, null=True)
In your Order model, you have defined a ForeignKey relationship for several other models (Annual, Issue, and Article), but each of these relationships points to the Catalog model. When you attempt to save the Order instance created by your form, it has received objects of these types (Annual, Issue, and Article), but it cannot store a foreign-key reference to these objects in the fields defined on the Order model. This is due to the foreign-key fields on the Order demanding that they can only contain a reference to Catalog objects.
If, for each of these foreign-key relationships, you wish to store one of these various kinds of objects, you will need to alter your Order model definition to expect references to objects of those models rather than Catalog objects.
In brief, I would suggest that the Order model be modified to include the following relationships. This will allow an order object to store a single reference to an object of each other kind (Annual, Issue, and Article).
annuals = models.ForeignKey(Annual, related_name='annuals_ordered', blank=True, null=True)
issues = models.ForeignKey(Issue, related_name='issues_ordered', blank=True, null=True)
articles = models.ForeignKey(Article, related_name='items_ordered', blank=True, null=True)
For more information about ForeignKey relationships in Django, see the reference here.