Django Form: form with ForeignKey - django

I'm making online shopping mall using Django(1.9.7) framework.
I think that showing codes is much easier than explaining in text.
models.py
class Product(TimeStampedModel):
name = models.CharField(max_length=120, unique=True)
slug = models.SlugField(null=True, blank=True)
description = models.TextField(max_length=400, blank=True)
is_active = models.BooleanField(default=True)
def __str__(self):
return self.name
class Variation(TimeStampedModel):
COLOR_CHOICES = (
('black', '흑백'),
('single', '단색'),
('multi', '컬러'),
)
price = models.DecimalField(
decimal_places=0,
max_digits=15,
blank=True,
null=True,
)
product = models.ForeignKey(Product)
color = models.CharField(
max_length=10,
choices=COLOR_CHOICES,
)
is_active = models.BooleanField(default=True)
class Meta:
unique_together = (('product', 'color'))
def __str__(self):
return str(self.product) + ' - ' + self.get_color_display()
I create form in my product_detail view and pass it as context data to template.
views.py
class ProductDetailView(DetailView):
model = Product
context_object_name = "product"
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
product = self.get_object()
context['cartitem_form'] = CartItemForm(product)
return context
What I want to do through form:
I want to show variations only related with given product. So I pass product as argument of form in view and save this product. And I'm trying to set the variation queryset through ModelChoiceField:
class CartItemForm(forms.ModelForm):
variation = forms.ModelChoiceField(
queryset=Variation.objects.filter(product=self.product)
)
class Meta:
model = CartItem
fields = (
'variation',
'width',
'height',
'quantity',
)
def __init__(self, *args, **kwargs):
self.product = kwargs.pop('product')
super().__init__(*args, **kwargs)
def save(self):
cart_item = super().save(commit=False)
cart_item.save()
return cart_item
but it doesn't work. How can I implement this?

Related

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

Django Forms - how to add the + sign for a Many2Many field

enter image description hereRelated to Django Forms and Many2Many
I have tried to look for ways to add the + to my django form when I want to add a new post to my webpage.
What I'm looking for is a similar function as the one in admin module. See picture.
I have tried to read though the docs but not sure what to look for. Have anybody build something similar?
my model looks like this
class Wine_taste(models.Model):
title = models.CharField(max_length=128, null=True, blank=True)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
category = models.ManyToManyField('Wine_Category', verbose_name='Kind of Wine',related_name='wcategory')
subcategory = models.ForeignKey('Wine_SubCategory', verbose_name='Wine type',on_delete=models.SET_NULL, null=True, blank=True)
review = models.TextField()
producer_taste = models.ForeignKey('Wine_Producer', verbose_name='Producer or shipper', on_delete=models.SET_NULL, null=True, blank=True )
grape_taste = models.ManyToManyField('Wine_Grapes', verbose_name='Grapes')
country_taste = models.ForeignKey('Wine_Country', verbose_name='Country of origin', on_delete=models.SET_NULL, null=True, blank=True )
sweetness = models.PositiveSmallIntegerField(choices=LEVELRATE_CHOICES, verbose_name='Rate Sweetness')
acid = models.PositiveSmallIntegerField(choices=LEVELRATE_CHOICES, verbose_name='Rate acid level')
fruit = models.PositiveSmallIntegerField(choices=LEVELRATE_CHOICES, verbose_name='Rate fruitness')
taste = models.PositiveSmallIntegerField(choices=RATE_CHOICES, verbose_name='Rate taste')
overall = models.PositiveSmallIntegerField(choices=RATE_CHOICES, verbose_name='Overall rate')
thumbnail = models.ImageField()
timestamp = models.DateTimeField(auto_now_add=True)
featured = models.BooleanField()
previous_post = models.ForeignKey('self', related_name='previous', on_delete=models.SET_NULL, null=True, blank=True)
next_post = models.ForeignKey('self', related_name='next', on_delete=models.SET_NULL, null=True, blank=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('wine:wine-dyn', kwargs={"id": self.id})
def get_update_url(self):
return reverse('wine:wine-update', kwargs={"id": self.id})
def get_delete_url(self):
return reverse('wine:wine-delete', kwargs={"id": self.id})
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.thumbnail.path)
if img.height > 400:
new_height = 400
new_width = new_height / img.height * img.width
output_size = ((new_height, new_width))
img.thumbnail(output_size)
img.save(self.thumbnail.path)
#property
def get_comments(self):
return self.comments.all().order_by('-timestamp')
#property
def comment_count(self):
return Comment.objects.filter(post=self).count()
#property
def view_count(self):
return PostView.objects.filter(post=self).count()
Blockquote
this is my view:
def wine_detailed_create(request):
title = 'Create'
form = CreateForm(request.POST or None, request.FILES or None)
author = get_author(request.user)
if request.method == 'POST':
if form.is_valid():
form.instance.author = author
form.save()
return redirect (reverse('wine:wine-dyn', kwargs={'id': form.instance.id}))
context = {
'title': title,
'form': form
}
return render(request, 'wine/wine_create.html', context)
and my forms:
class CreateForm(forms.ModelForm):
class Meta:
model = Wine_taste
fields = ('title',
'country_taste',
'category',
'subcategory',
'producer_taste',
'grape_taste',
'review',
'thumbnail',
'sweetness',
'acid',
'fruit',
'taste',
'overall',
'featured',
'previous_post',
'next_post',)
Blockquote
br
Lars

Django rest framework API with filter

Models.py
Categories:
class Category_product(models.Model):
category_name = models.CharField(max_length=200, unique=True)
def __str__(self):
return self.category_name
Products:
class Warehouse(models.Model):
category_product = models.ForeignKey(
Category_product, on_delete=models.CASCADE)
product_name = models.CharField(max_length=200, unique=True)
condition = models.BooleanField(default=False)
amount = models.IntegerField()
barcode = models.BigIntegerField()
f_price = models.CharField(max_length=255, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.product_name
urls.py
path('products-list/', views.WarehouseList.as_view()),
Views.py
class WarehouseList(generics.ListCreateAPIView):
queryset = Warehouse.objects.all()
serializer_class = WarehouseSerializer
Serializers.py
# SERIALIZER OF CATEGORY PRODUCTS
class Category_productSerializer(serializers.ModelSerializer):
class Meta:
model = Category_product
fields = ['id', 'category_name']
# SERIALIZER OF WAREHOUSE
class WarehouseSerializer(serializers.ModelSerializer):
category_name = serializers.ReadOnlyField(
source='category_product.category_name')
def get_serializer(self, *args, **kwargs):
if isinstance(kwargs.get('data', {}), list):
kwargs['many'] = True
return super(Category_productSerializer, self).get_serializer(*args, **kwargs)
class Meta:
model = Warehouse
fields = ['id', 'category_product', 'category_name', 'condition',
'product_name', 'amount', 'barcode', 'f_price', 'created_at', 'updated_at']
I want to get products by exact category
For example:
I have product category
{
"id": 1
"category_name": "Electronics"
}
If I send GET request to api/products-list/?cat=1
I want to get products which have this category
Create a get_queryset method as follow.
class WarehouseList(generics.ListCreateAPIView):
queryset = WareHouse.objects.all()
serializer_class = WarehouseSerializer
def get_queryset(self):
cat = self.request.query_params.get('cat', None)
if cat is not None:
self.queryset = self.queryset.filter(category_product__id=cat)
return self.queryset

How to update data in django model to title case in views?

I'm trying to change a column of strings like 'HOSPITAL ZERO', "HOSPITAL ONE" from the database into title case or 'Hospital zero' in the views.py or models.py. I've tried both and can't get either to work for me.
Here is my code in views.py. Column is in Hospital Model under name, i.e. Hospital.name.
def results(request):
if request.method == "GET":
Q=()
out_words = request.GET.get('s')
context = RequestContext(request)
#here is where I tried to change it
qs = Price.objects.annotate(hospital__name=Lower('hospital__name'))
table = PriceTable(qs.filter(service__desc_us__icontains = out_words))
RequestConfig(request, paginate={'per_page': 10}).configure(table)
RequestConfig(request).configure(table)
else:
table = PriceTable(Price.objects.all())
RequestConfig(request).configure(table)
return render(request, 'results.html', {'table': table})
Here is how I tried in model.py.
class Hospital(models.Model):
"""Model representing Hospitals."""
hid = models.CharField(max_length = 8, null=True)
name = models.CharField(max_length=200, primary_key=True)
hopid = models.UUIDField(default=uuid.uuid4, help_text='Unique ID for this particular hospital in database')
address = models.CharField(max_length = 200, null = True)
class Meta:
ordering = ['hopid']
#here is where i tried to update it
def save(self, *args, **kwargs):
self.name = self.name.title()
return super(Hospital, self).save(*args, **kwargs)
def __str__(self):
"""String for representing the Model object."""
return f'{self.name} ({self.address})'
class Price(models.Model):
"""Model with all the hospital prices by service."""
priceid = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text='Unique ID for this particular service in database')
comments = models.CharField(max_length = 200, blank = True, null =True)
hospital = models.ForeignKey("Hospital", on_delete=models.SET_NULL, null=True)
class Meta:
ordering =['priceid']
def __str__(self):
return f'{self.hospital.name} (...)'
You can try this:
"HOSPITAL ONE".lower().capitalize()
my_string.lower().capitalize()
Here is an option:
def save(self, *args, **kwargs):
self.name = self.name.lower().capitalize()
return super(Hospital, self).save(*args, **kwargs)
Here's a new method for your Hospital model called get_title_case_name. Now you can access a hospital name by calling hospital_instance.get_title_case_name().
class Hospital(models.Model):
"""Model representing Hospitals."""
hid = models.CharField(max_length = 8, null=True)
name = models.CharField(max_length=200, primary_key=True)
hopid = models.UUIDField(default=uuid.uuid4, help_text='Unique ID for this particular hospital in database')
address = models.CharField(max_length = 200, null = True)
class Meta:
ordering = ['hopid']
def __str__(self):
"""String for representing the Model object."""
return f'{self.name} ({self.address})'
def get_title_case_name(self):
return self.name.capitalize()
Just add method to model:
def get_title_case_name(self):
return self.name.title()
In views/template you can run this method against model object.

Django CreateView filter foreign key in select field

I need some help with Django 2 and Python 3.
I'm using a CreateView to add new reccords in my database, but I need to make a filter for my Aviso form page to make the select field (field turma) to show only instances where the representante is the current user.
This is my model:
class Turma(models.Model):
nome = models.CharField(max_length=120, blank=False, null=False, help_text='Obrigatório.')
alunos = models.ManyToManyField(User, help_text='Obrigatório', related_name='alunos_matriculados')
data_cadastro = models.DateField(auto_now_add=True)
representante = models.ForeignKey(User, on_delete=models.PROTECT, blank=False, null=False)
colegio = models.ForeignKey(Colegio, on_delete=models.PROTECT, blank=False, null=False, help_text='Obrigatório.')
class Aviso(models.Model):
data_final = models.DateField(auto_now=False, auto_now_add=False, blank=False, null=False, verbose_name="Data Final")
comentarios = models.TextField(null=True, blank=True)
ultima_modificacao = models.DateField(auto_now=True)
data_post = models.DateField(auto_now_add=True)
turma = models.ForeignKey(Turma, on_delete=models.PROTECT, null=False, blank=False)
materia = models.ForeignKey(Materia, on_delete=models.PROTECT, null=False, blank=False)
This is my view:
class AvisoCreateView(LoginRequiredMixin, CreateView): #Cadastro de Aviso
template_name = 'form.html'
model = models.Aviso
login_url = '/login/'
success_url = reverse_lazy('visualizar_aviso')
fields = [
'turma',
'materia',
'tipo_aviso',
'comentarios',
'data_final'
]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['titulo'] = 'Cadastrar aviso'
context['input'] = 'Adicionar'
return context
How could that be done?
You can add a queryset to the ForeignKey field.
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'].fields['turma'].queryset = Turma.objects.filter(representante=self.request.user)
context['titulo'] = 'Cadastrar aviso'
context['input'] = 'Adicionar'
return context
You could create a ModelForm for that model.
And based on this answer you could override the forms __init__() method to alter the fields queryset.
class AvisoForm(forms.ModelForm):
class Meta:
model = Aviso
fields = [
'data_final', 'comentarios', 'ultima_modificacao', 'data_post',
'turma', 'materia',
]
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
# restrict the queryset of 'Turma'
self.fields['turma'].queryset = self.fields['turma'].queryset.filter(
representante=user)
Then, in your view, replace the attribute fields with form_class:
class AvisoCreateView(LoginRequiredMixin, CreateView):
...
form_class = AvisoForm
...
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
Maybe you need to adjust a few things for your specific case.
Let us know if that solved it.