Django - how to exclude repeating objects - django

models.py
class Location(models.Model):
name = models.CharField(max_length=100, verbose_name=u"Локация", default=u'')
country = models.CharField(max_length=100, verbose_name=u"Страна", default=u'')
class Country(models.Model):
name = models.CharField(max_length=50, verbose_name=u"Страна")
class Photo(models.Model):
location = models.ForeignKey(Location, null=True, verbose_name=u'Фото')
photo = models.ImageField(upload_to='photos', null=True)
forms.py
class LocationForm(forms.ModelForm):
class Meta:
model = Location
fields = ['name', 'country']
photos = MultiFileField(min_num=1, max_num=10)
def save(self, commit=True):
instance = super(LocationForm, self).save(commit)
for each in self.cleaned_data['photos']:
Photo.objects.create(photo=each, location=instance)
return instance
views.py
class AddLocationPageView(CreateView):
model = Location
form_class = LocationForm
template_name = 'add_location.html'
class BrowseLocationsPageView(ListView):
model = Country
context_object_name = 'countries'
template_name = "browse_locations.html"
add_location.html
<form action="" method="POST">{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-default" type="submit">Add</button>
</form>
browse_locations.html
{% for country in countries %}
{{ country }}
{% endfor %}
I need to get in browse_locations.html the list of countries without repeating.
For exapmple, I am creating location object with country 'USA'. Then I am creating second one with country 'USA'. And in my view I can see the list where there are two 'USA', but I need only one.
Thanks!!!

If you are using postgres, then you can use the distinct queryset filter with a field name.
class BrowseLocationsPageView(ListView):
def get_queryset(self):
return self.model.objects.distinct('name')
This solves the "issue" but there is a bigger problem at hand. Wherever you are creating the countries, you are creating new countries instead of looking if there is an existing country with the same name - get_or_create may be useful here.
Location should probably have a foreign key to a country too...

You have country as a CharField in the Location Model. Hence the repitition.
Change the country field as a ForeignKey in the Location model

Related

Django form to formset

I'm currently learning Django forms and I came across this post.
One of the forms currently looks like this:
What I'd like to do is to change Category into a formset and be able to render multiple dropdowns while creating a product.
My models.py:
class Category(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=30)
price = models.DecimalField(decimal_places=2, max_digits=10)
category = models.ForeignKey(Category, on_delete = models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.name
My forms.py:
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
fields = ('name', )
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ('name', 'price', 'category', )
def __init__(self, user, *args, **kwargs):
super(ProductForm, self).__init__(*args, **kwargs)
self.fields['category'].queryset = Category.objects.filter(user=user)
Current method in views.py:
#login_required
def new_product(request):
if request.method == 'POST':
form = ProductForm(request.user, request.POST)
if form.is_valid():
product = form.save(commit=False)
product.user = request.user
product.save()
return redirect('products_list')
else:
form = ProductForm(request.user)
return render(request, 'products/product_form.html', {'form': form})
products_form.html:
{% extends 'base.html' %}
{% block content %}
<h1>New product</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="save">
cancel
</form>
{% endblock %}
What I tried is to make use of the modelformset_factory and change the method in views.py by creating a CategoryFormSet as:
CategoryFormSet = modelformset_factory(Category, fields=('name', ), extra=2)
formset = CategoryFormSet(data=data, queryset=Category.objects.filter(user=request.user))
then replacing the original form from views.py with the created formset. In the html I simply replace the {{form}} with {{formset}}. After playing around with it for a while, I either get the New product with just a submit button (no form rendered) or a User object has no attribute GET error. What am I doing wrong?
The tutorial focuses on allowing the user to add/update more instances of one model. You want to edit one thing, with multiple related things inline.
However, your data model only allows one category per product, so this does not make any sense. Whether you want more than one category per product, is something only you can answer :) - I'm going to assume you want that.
First you need to change your model to allow for multiple categories per product:
class Product(models.Model):
name = models.CharField(max_length=30)
price = models.DecimalField(decimal_places=2, max_digits=10)
categories = models.ManyToManyField(Category, related_name='products')
user = models.ForeignKey(User, on_delete=models.CASCADE)
And then you need to learn about Inline Formsets.
Come back with a specific if you get stuck on that.
Instead of creating new model Category. You can do this.
CATEGORY_CHOICES= (
("1", "1"),
("2", "2"),
("3", "3"),
("4", "4"),
("5", "5"),
("6", "6"),
("7", "7"),
("8", "8"),
)
category = models.CharField(max_length = 20,choices = CATEGORY_CHOICES,default = '1')
It will automatically render in HTML.

Django: want to loop through _set for pk values only

I'm stuck trying to figure out how to filter my template values with the detail view PK. I have a detail view for my employee. I wish to display my employee's subjects, where I then wish to filter the subjects with the evaluations that have been made for the subject.
I've gotten so far that I can show my subject names, and show all the evaluations for each subject. However, I don't want to show ALL of them I only want to show the ones that exist for the current employee (detailView PK). As you can see in my template, I'm using _set to make the relation, but I have no clue on how to filter the PK into that equation.
Example, what I want:
Subject 1:
Evaluationname - employee Johnny
Evaluationname - employee Johnny
Example, what I currently have:
Subject 1:
Evaluationname - employee Johnny
Evaluationname - employee Chris
I don't want Chris's evaluation, I only wish to filter the primary key, so in this case Johnny's evaluations.
Template
{% for subject in subject_list %}
{{ subject.subejctname }}
{% for evaluation in subject.evaluation_set.all %}
<div>
<p>{{ evaluering.ma }} | {{ evaluering.ma.firstname }} | {{ evaluering.ma.lastname }}</p>
</div>
{% empty %}
<p>No evaluations founds.</p>
{% endfor %}
{% endfor %}
View
class EmployeeDetailView(DetailView):
template_name = 'evalsys/employee/alle_employees_eval.html'
model = Employee
# Uses employee PK to make a detail view
def view_employee_with_pk(self, pk=None):
if pk:
employee = Employee.objects.get(pk=pk)
else:
employee = self.employee
args = {'employee': employee, }
return render(self, 'evalsys/employee/alle_employees_eval.html', args)
def get_context_data(self, **kwargs):
context = super(EmployeeDetailViewDetailView, self).get_context_data(**kwargs)
context['subject_list'] = Subject.objects.all()
return context
Subject Model
class Subject(models.Model):
id = models.AutoField(primary_key=True)
subjectname = models.CharField(max_length=255, help_text="Indtast navnet på faget.")
slug = models.SlugField(max_length=200, unique=True)
Evaluation model
class Evaluation(models.Model):
id = models.AutoField(primary_key=True)
employee_num = models.ForeignKey('Employee', on_delete=models.CASCADE, null=True)
subjectname = models.ForeignKey('Subject', on_delete=models.CASCADE, null=True)
Employee model
class Employee(models.Model):
id = models.AutoField(primary_key=True)
slug = models.SlugField(max_length=200)
employee_num = models.IntegerField(help_text="Indtast medarbejderens MA-nummer. (F.eks 123456)")
firstname = models.CharField(max_length=30, help_text="Indtast medarbejderens fornavn.")
lastname = models.CharField(max_length=30, help_text="Indtast medarbejderens efternavn.")
subjectname = models.ForeignKey('Subject', on_delete=models.CASCADE, null=True)
Reverse relationships (subject.evaluation_set) can be prefetched, this is a technique for reducing the number of database queries made when you access the reverse relationship for many objects in a queryset. When using the following queryset, when you access subject.evaluation_set.all it will not perform an additional DB access as the result has already been cached on each instance
Subject.objects.all().prefetch_related('evaluation_set')
This cached result can be modified by using Prefetch objects. Using these you can limit the contents of subject.evaluation_set.all to only contain the result that you want
Subject.objects.all().prefetch_related(
Prefetch(
'evaluation_set',
queryset=Evaluation.objects.filter(employee=self.employee)
)
)
Your model structure is confusing. Are you able to detail the relationship between employee, subject and evaluation?? You have mentioned you wish to display an employee's subjects, but via your model structure, an employee can have only one subject, as employee is related to the subject by a foreign key.
Below I have suggested some changes to your model names and your model structure so it might make more sense to retrieve your evaluations in the template. Feel free to ask questions about your model design as that is crucial to design your views, templates, etc.
Also please refer here for model naming conventions
Django Foreign Key Reference
Django Model Coding Style (PEP8)
Subject Model
class Subject(models.Model):
id = models.AutoField(primary_key=True)
subject_name = models.CharField(max_length=255, help_text="Indtast navnet på
faget.")
slug = models.SlugField(max_length=200, unique=True)
Employee Model
class Employee(models.Model):
id = models.AutoField(primary_key=True)
slug = models.SlugField(max_length=200)
employee_num = models.IntegerField(help_text="Indtast medarbejderens MA-nummer. (F.eks 123456)")
first_name = models.CharField(max_length=30, help_text="Indtast medarbejderens fornavn.")
last_name = models.CharField(max_length=30, help_text="Indtast medarbejderens efternavn.")
subjects = models.ManyToManyField(Subject, related_name='employee', through='Evaluation')
Evaluation Model
class Evaluation(models.Model):
name = models.CharField(blank=True,max_length=50)
employee = models.ForeignKey('Employee', on_delete=models.CASCADE)
subject = models.ForeignKey('Subject', on_delete=models.CASCADE)
So the assumption, is an employee can have different subjects and the mapping is defined via a through model using many-to-many.
Your DetaiView can then just be
class EmployeeDetailView(DetailView):
template_name = 'evalsys/employee/alle_employees_eval.html'
model = Employee
def get_context_data(self, **kwargs):
context = super(EmployeeDetailViewDetailView,
self).get_context_data(**kwargs)
context['evaluations'] = Evaluation.objects.filter(employee=self.object)
return context
Template
{% for evaluation in evaluations %}
{{ evaluation.subject.subject_name }}
<p>{{ evaluation.name }} | {{ evaluation.employee.first_name }} |
{{evaluation.employee.last_name }}</p>
{% empty %}
<p>No evaluations founds.</p>
{% endfor %}

Django Formset: multichoice queryset filter is not working for my formset after many attempts

I have Contestant model that has many-to-one relationship with Category. I want the creator of the award to only have access to last 15 categories of the award he has created in instantiating contestant model. That's, the multichoice queryset in the Contestant field(category) will only show the list of the last 15 categories being created in the Category Model. I have made different efforts, but the code is either not working or giving '['ManagementForm data is missing or has been tampered with']' error.
I have made countless re-factorization of codes and I have tried to adopt solutions I found on internet. But they didn't work.
# MY CATEGORY MODEL
class Category(models.Model):
award = models.ForeignKey(Award, on_delete=models.CASCADE)
award_category = models.CharField(max_length=150, blank=True, null=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
def __str__(self):
return self.award_category
# MY CONTESTANT MODEL
class Contestant(models.Model):
award_name = models.ForeignKey(Award, on_delete=models.CASCADE)
contestant_name = models.CharField(max_length=150, null=True, blank=True )
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='contestants')
vote = models.IntegerField()
def number_of_vote(self):
return Contestant.objects.filter(self.vote).count()
vote_count = property(number_of_vote)
#MY VIEW
def get_award(request, pk):
new_award = get_object_or_404(Award, pk=pk)
Contest_Formset = modelformset_factory(Contestant, fields('contestant_name', 'category',), extra=15)
formset = Contest_Formset(request.POST)
for form in formset:
form.fields['category'].queryset = Category.objects.filter(user=request.user)[1:15]
if request.method == 'POST' and form.is_valid():
myform = form.save(commit=False)
myform.award_name = new_award
myform.save()
return redirect('award_details', pk=new_award.pk)
else:
formset = Contest_Formset()
context = {
'new_award': new_award,
'formset': formset
}
return render(request, 'voting/get_award.html', context)
# TEMPLATE
<form action=" " method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ formset.management_form }}
{{ formset }}
<input type="submit" value="save">
</form>
I expect the output of the contestant's category field to show only the last 15 categories created by the login user.

return objects pointing to other objects in django

I have a model UserSelect
class UserSelect(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
bowler = models.ForeignKey(Bowlers, on_delete=models.CASCADE, related_name='bowlers', null=True, blank=True)
batsman = models.ForeignKey(Batsmen, on_delete=models.CASCADE, related_name='batsman', null=True, blank=True)
team = models.IntegerField(blank=True, default='1')
which is pointing to my two models bowler and batsman using foreign key
I am calling this model values in my view as
def team(request, pk):
user_sel = UserSelect.objects.filter(user=request.user, team=pk)
context = {
'user_sel': user_sel,
}
return render(request, 'team_analysis.html', context)
here I am getting all objects by giving pk but how do I access data of my other two models bowler and batsman through it in views?
You can access them in views like this:
for user in user_sel:
print(user.bowler) # bowler model object
print(user.batsman) # batsman model object
Also you can access them in templates as well:
{% for user in user_sel %}
{{ user.bowler }}
{{ user.bowler.pk }}
{{ user.batsman }}
{{ user.batsman.pk }}
{% endfor %}

Django1.6 UpdateView, how to display field without update it

models.py
class Application(models.Model):
desc = models.CharField(max_length=40, unique=True, null=False)
name = models.CharField(max_length=40, null=False)
user = models.ForeignKey(User)
views.py
class ApplicationUpdate(UpdateView):
model = Application
template_name = "apps/update.html"
fields = ['name', 'desc']
context_object_name = "app"
success_url = reverse_lazy('app-list')
templates
<div class='update-item'>
{{form.name.error}}
{{form.name.label_tag}}
{{form.name}}
</div>
<div class='update-item'>
{{form.desc.value}}
</div>
Here I want to display desc field in template, but only POST name field when update it. Any solution for it?
Try one of these:
Set readonly attributes on fields that you don't want to be changed.
Instead of {{form.desc.value}} you can display instance field value - {{ form.instance.desc }}, next remove desc field from include attribute.