How to add condition on Django model foreign key? - django

I am new in Django, would you please help me, I have two models, by name of Continent and Country, in the Country form I want to only display the Continents in the dropdown list which their status is true?
models
from django.db import models
from smart_selects.db_fields import GroupedForeignKey, ChainedForeignKey
class Continent(models.Model):
name = models.CharField(max_length=255)
status=models.BooleanField(default=True)
def __str__(self):
return self.name
class Country(models.Model):
continent = models.ForeignKey(Continent, null=True, on_delete=models.SET_NULL)
status=models.BooleanField(default=True)
name = models.CharField(max_length=255)
def __str__(self):
return self.name
forms
class FormContinent(ModelForm):
class Meta:
model = Continent
fields = '__all__'
class FormCountry(ModelForm):
class Meta:
model = Country
fields = '__all__'
views
def continent(request):
form = FormContinent()
if request.method == 'POST':
form = FormContinent(request.POST)
form.is_valid()
form.save()
return redirect('/continent')
else:
context = {'form': form}
return render(request, 'continent.html', context)
def country(request):
form = FormCountry()
if request.method == 'POST':
form = FormCountry(request.POST)
form.is_valid()
form.save()
return redirect('/country')
else:
context = {'form': form}
return render(request, 'country.html', context)

You can make use of the limit_choices_to=… parameter [Django-doc]:
class Country(models.Model):
continent = models.ForeignKey(
Continent,
# ↓ limit choices to Continents with status=True
limit_choices_to={'status': True},
null=True,
on_delete=models.SET_NULL
)
# …

Related

Django form in form

I wonder if it is possible to make form in form by using django.
For example:
class Category(models.Model):
title = models.CharField(max_length=200)
icon = models.ImageField(upload_to="icons", default="icons/dot.png")
def __str__(self):
return self.title
def get_posts(self):
return Post.objects.filter(category__title=self.title)
class Post(models.Model):
author = models.ForeignKey(Account, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
description = models.CharField(max_length=400)
date_posted = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
While I was working with forms based on these models I was splitting CategoryForm and PostForm into two forms (category had to be made before post to make user able to choose new category during making new post) and then into two views.
class PostForm(forms.ModelForm):
class Meta:
model = Post
exclude = ('author',)
class CategoryForm(forms.ModelForm):
class Meta:
model = Catergory
fields = '__all__'
def newPostView(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.author = request.user
obj.save()
return redirect('home')
form = PostForm()
context = {'form':form}
return render(request, 'blog/post_form.html', context)
def newCategoryView(request)
...
...
...
I'd like to make one newPost form which will have Category form implemented - I mean I'd like to be make it possible to choose category from already made categories or make new one if its needed without using another view
How can I make it ?

M2M relation ship : Can't save to the through model

i have a an order model which is in m2m relationship with a product model, when i create an order, and after checking my DB, i can see the order saved but not in the through model
models.py
from inventory.models import Product
from user.models import User
class Order(models.Model):
product = models.ManyToManyField(Product, through='OrderItems' )
vendeur = models.ForeignKey(User, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField()
class Customer(models.Model):
full_name = models.CharField(max_length=60, verbose_name='full name')
address = models.CharField(max_length=255)
phone = models.CharField(max_length=20)
city = models.CharField(max_length=30)
class OrderItems(models.Model):
product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)
order = models.ForeignKey(Order,on_delete=models.CASCADE)
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, null=True)
views.py
#login_required
def add_order(request):
if request.method == 'POST':
form = NewOrderForm(request.POST)
if form.is_valid():
order = form.save(commit=False)
order.vendeur = request.user
order.save()
return redirect('dashboard-index', )
else :
form = NewOrderForm()
return render(request, 'dashboard/add_order.html', {'form': form})
forms.py
class NewOrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ('product','quantity')
if you use save(commit=False), Calling save_m2m() is required.
because your form has a m2m field
refer to documentions
#login_required
def add_order(request):
if request.method == 'POST':
form = NewOrderForm(request.POST)
if form.is_valid():
order = form.save(commit=False)
order.vendeur = request.user
order.save()
form.save_m2m()
return redirect('dashboard-index', )
else :
form = NewOrderForm()
return render(request, 'dashboard/add_order.html', {'form': form})

ModelForm inserts number in foreign key field

I have model from which I created a ModelForm:
models.py:
class City(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return f'{self.name}'
class Profile(models.Profile):
name = models.CharField(max_length=50)
user = models.OneToOneField(User, on_delete=models.CASCADE, unique=False)
location = models.ForeignKey('City', on_delete=models.SET_NULL, blank=True, null=True)
forms.py
from django import forms
from .models import Profile, City
class LocationField(forms.CharField):
def clean(self, value):
try:
city = City.objects.get(name=value)
except ObjectDoesNotExist:
city = City.objects.create(name=value)
return city
class ProfileForm(forms.ModelForm):
location = LocationField()
class Meta:
model = Profile
exclude = ['user']
views.py
def profile_update_view(request):
template_name = 'profiles/update.html'
user = request.user
profile = Profile.objects.get(user__id=user.id)
if request.method == 'GET':
form = ProfileForm(instance=profile)
else:
form = ProfileForm(request.POST, instance=profile)
if form.is_valid():
obj = form.save(commit=False)
obj.user = user
obj.save()
return redirect('profile_view')
context = {'form': form}
return render(request, template_name, context=context)
When I'm saving form, I'm satisfied how it's working, but when I load form again to update in, it fills LocationField() as an City pk integer, but I want it to load name instead. Is there a way to do this?
I've added in views.py:
if request.method == 'GET':
initial = {}
if profile.location:
initial = {'location': profile.location.name}
form = ProfileForm(instance=profile, initial=initial)
now it's working. But it's some workaround. I've thought there is some parameter maybe

Django - ModelForm initial data from database

In my Django project i create an app to have additional information about registered users. So my model looks like this:
class UserProfile(models.Model):
class Meta:
verbose_name_plural = u'User Profile'
user = models.OneToOneField(User)
birthday = models.DateField(blank=True, null=True)
avatar = models.ImageField(upload_to='media/profile/avatar', blank=True, null=True)
name = models.CharField(blank=True, null=True, max_length=20)
surname = models.CharField(blank=True, null=True, max_length=50)
phone = models.CharField(blank=True, null=True, max_length=12)
def __unicode__(self):
return '%s' % self.user
In user profile i create modelform where user can fill or edit the fields from UserProfile model:
class ExtraProfileDataForm(ModelForm):
name = forms.CharField(label=(u'Enter your name'))
surname = forms.CharField(label=(u'Enter your surname'))
phone = forms.CharField(label=(u'Enter your phone'))
birthday = forms.DateField(label=(u'Enter birthday'))
avatar = forms.ImageField(label=(u'Enter avatar'))
class Meta:
model = UserProfile
fields = ('name', 'surname', 'phone', 'birthday', 'avatar')
def __init__(self, *args, **kwargs):
super(ExtraProfileDataForm, self).__init__(*args, **kwargs)
for key in self.fields:
self.fields[key].required = False
This is the view of the model form:
#login_required
def UserFullDataForm(request):
if request.method == 'POST':
form = ExtraProfileDataForm(request.POST)
if form.is_valid():
profile_user = request.user
user_profile = UserProfile(user=profile_user)
user_profile.name = form.cleaned_data['name']
user_profile.surname = form.cleaned_data['surname']
user_profile.phone = form.cleaned_data['phone']
user_profile.birthday = form.cleaned_data['birthday']
user_profile.avatar = form.cleaned_data['avatar']
user_profile.save()
return HttpResponseRedirect('/profile/')
else:
return render(request, 'profiles/extra_profile.html', {'form':form})
else:
form = ExtraProfileDataForm()
context = {'form':form}
return render (request, 'profiles/extra_profile.html', context)
But i want to load on ExtraProfileDataForm initial data from model UserProfile if the fields not empty. I searched how to do that on Django documentation website, but nothing found. Can somebody help me to understand how to do it? Thanks a lot.
You use the instance parameter.
Note that you are doing much more work than necessary here; most of your view can be cut.
#login_required
def UserFullDataForm(request):
try:
profile = request.user.userprofile
except UserProfile.DoesNotExist:
profile = UserProfile(user=request.user)
if request.method == 'POST':
form = ExtraProfileDataForm(request.POST, instance=profile)
if form.is_valid():
form.save()
return HttpResponseRedirect('/profile/')
else:
form = ExtraProfileDataForm(instance=profile)
return render(request, 'profiles/extra_profile.html', {'form':form})
Similarly, in your form, you don't need the overridden __init__ method because you're manually specifying all the fields anyway; you can add required=False on each one there. However, you could make this even shorter by adding the labels in the model definition; then your entire modelform could just be:
class ExtraProfileDataForm(ModelForm):
class Meta:
model = UserProfile
fields = ('name', 'surname', 'phone', 'birthday', 'avatar')
One final note: you're consistently using three-space indentation, which is a bit, well, odd. Most Python programmers prefer two or four.

django: ForeignKeyField limit_choices_to "parents pk"?

I've a model (Parent model):
class Post(models.Model):
image = models.ImageField(upload_to='%Y/%m/%d')
title = models.CharField(max_length=200)
width = models.DecimalField(max_digits=3, decimal_places=0)
height = models.DecimalField(max_digits=3, decimal_places=0)
year = models.PositiveIntegerField()
def __str__(self):
return self.title
and another model (Child model):
class Addimg(models.Model):
post = models.ForeignKey('Post', null=True)
addimg = models.ImageField(upload_to='%Y/%m/%d')
def __str__(self):
return self.post
My Addimg Form:
class AddimgForm(forms.ModelForm):
class Meta:
model = Addimg
fields = ('post', 'addimg', 'width', 'height',)
views.py using the form:
def addimg(request, pk):
if request.method == "POST":
form = AddimgForm(request.POST, request.FILES)
post = get_object_or_404(Post, pk=pk)
if form.is_valid():
addimg = form.save(commit=False)
addimg.addimg = request.FILES['addimg']
addimg.save()
return redirect('blog.views.detail', pk=post.pk)
else:
form = AddimgForm()
return render(request, 'blog/edit.html', {'form': form})
And my Problem is that when I create a "Child model" my post field returns all instances of allready created Post models as choices. What I want is that it automatic only displays the one Post it is related to without choices. Is ForeignKey the right model for that?
Any Ideas how this could work. thanks
ForeignKey field is translated into ModelChoiceField inside a Django ModelForm. If you inspect that class you will notice that this type of field has an queryset attribute required. By default Django provides the full set of objects. You can override this inside your form __init__ method by providing the parent object the form will need.
Consider the following example code:
def addimg(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = AddimgForm(request.POST, request.FILES, post=post)
#...
else:
form = AddimgForm(post=post)
return render(request, 'blog/edit.html', {'form': form})
class AddimgForm(forms.ModelForm):
class Meta:
model = Addimg
fields = ('post', 'addimg', 'width', 'height',)
def __init__(self, *args, **kwargs):
post = kwargs.pop('post')
super(AddimgForm, self ).__init__(*args, **kwargs)
self.fields['post'].queryset = Post.objects.filter(id=post.id)
What you want to do is create a Many-to-one relationship. For example,
post = models.ForeignKey('Post', null=True)
This means you can filter on it for example,
Addimg.objects.filter(post=Post)
or
Post.objects.get(pk=1)
Post.addimg_set.filter(xyz=etc)
Read more here: https://docs.djangoproject.com/en/1.8/topics/db/examples/many_to_one/