Django framework: ListView with CreateView - django

I am new in django programming, and I have simple question.
I have model
class Dhcp(models.Model):
class Meta:
ordering = ('gateway',)
verbose_name = _(u'DHCP Configuration')
verbose_name_plural = _(u'DHCP Configurations')
gateway = models.IPAddressField(_(u'Gateway'), null=True)
dns_primary = models.IPAddressField(_(u'DNS Primary'), null=True)
dns_second = models.IPAddressField(_(u'DNS Second'), blank=True)
leases_time = models.IntegerField()
nat = models.IPAddressField(_(u'NAT'), blank=True)
max_time = models.IntegerField()
def __unicode__(self):
return self.gateway
and my question is :
And I want to list all my dhcp objects which are in database, and add more object. But I want do that on same template.
For example: If I have 3 objects in database, show that and show form for add more obj. When I add one more object from form, I want to show the added object.
Thanks!
Dusan Ristic

Have a look into Class Based Views, more specifically the ListView and CreateView. This question comes up quite often, so you should find the answer on here somewhere. Check this post out.
What you're looking for is something like this:
class DHCPView(forms.CreateView):
model = Dhcp
template_name = "list_and_create_dhcp.html"
def get_context_data(self, **kwargs):
kwargs["object_list"] = Dhcp.objects.all()
return super(DHCPView, self).get_context_data(**kwargs)

Related

Django save multiple foreign keys to database with multiple upload

I am a newbie in Django and I am looking for the best workaround on how to deal with this problem.
I have these 3 Models:
class Rigs(models.Model):
name = models.CharField(max_length=30)
featured_img = models.ImageField(upload_to='images/')
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Gallery(models.Model):
rigs = models.ForeignKey(Rigs, on_delete=models.CASCADE, related_name='galleries')
file = models.ImageField(upload_to='images/')
def __str__(self):
return self.name
class Specs(models.Model):
cpu = models.CharField(max_length=50)
motherboard = models.CharField(max_length=50)
rigs = models.ForeignKey(Rigs, on_delete=models.CASCADE)
def __str__(self):
return self.cpu
I am using this library for the multi-uploading of images (https://github.com/Chive/django-multiupload), so basically I structured my Forms similar to what the document states:
class SpecsForm(forms.ModelForm):
class Meta:
model = Specs
fields = ('rigs', 'motherboard')
class RigForm(forms.ModelForm):
class Meta:
model = Rigs
fields = ('name','featured_img')
gallery_image = MultiMediaField(
min_num=1,
max_num=3,
max_file_size=1024*1024*5,
media_type='image'
)
def save(self, commit=True):
instance = super(RigForm, self).save(commit)
for each in self.cleaned_data['gallery_image']:
Gallery.objects.create(file=each, rigs=instance)
return instance
As well as my Views:
class newrig(CreateView):
model = Rigs
form_class = RigForm
template_name = 'flexrigapp/newrig.html'
success_url = '?success'
My problem is that I couldn't figure out how to include the SpecsForm on my HTML Form as well as save it on my database with the correct Foreign Key.
I have already tried transferring this one on my views with some changes but still no good.
def save(self, commit=True):
instance = super(RigForm, self).save(commit)
for each in self.cleaned_data['gallery_image']:
Gallery.objects.create(file=each, rigs=instance)
return instance
Also, I tried a function based views instead of class based but the problem is that the multi-uploading validation doesn't work on this approach of mine or maybe my code is not correct.
The expected result is that data should be saved on its respective databases (Rigs, Specs, Gallery) with the proper Foreign key on the Specs and Gallery. Right now, I can only save the Rigs and Gallery.
This is Django 2.2.4, by the way.
UPDATE
I tried updating my Views to Function-Based View just like what #dirkgroten suggested.
def newrig(request):
if request.method == 'POST':
rig_form = RigForm(request.POST, request.FILES, prefix="rigs")
specs_form = SpecsForm(request.POST, prefix="specs")
if rig_form.is_valid() and specs_form.is_valid():
rigs = rig_form.save()
specs = specs_form.save(commit=False)
specs.rigs = rigs
specs.save()
return HttpResponseRedirect("?success")
else:
rig_form = RigForm(prefix="rigs")
specs_form = SpecsForm(prefix="specs")
return render(request, "flexrigapp/newrig.html", {'rig_form': rig_form,'specs_form': specs_form,})
No data is being saved on the database now. No error logs.

Update several objects with formset

I'm 1st year on CS so forgive my noobness if i am totally unclear.
I have several objects from my "Products" Model. Now I would like to update the same field on all of the objects, the 'quantity' field with different values. But instead of clicking in and out of each product as with updateview, i would like to list all products and set the value for each and change them at the simultaneously. As far as i can see "FormSet" should do the trick?
My category model looks like this (to assign for a product)
class Category(models.Model):
category = models.CharField(max_length=30)
def __str__(self):
return self.category
My Product model looks like this:
class Product(models.Model):
title = models.CharField(max_length=200)
description = models.CharField(max_length=200)
category = models.ForeignKey(Category)
price = models.DecimalField(max_digits=5, decimal_places=2)
stock = models.PositiveIntegerField()
def __str__(self):
return self.title
My update view for updating single product looks like this:
class UpdateProductView(UpdateView):
model = Product
form_class = ProductForm
template_name = "product_form.html"
success_url = '/products'
class CreateCategoryView(FormView):
template_name = "category_form.html"
form_class = CategoryForm
I read the documentation on formset, but i gotta admit i didn't feel much smarter about how to actually use it afterwards.. can anyone give a hand?
Didn't find quantity field on your Product model, but as I can see, you want to use ModelFormSet.
# Generate your formset class with `modelformset_factory`
ProductFormSetClass = modelformset_factory(Product, fields=('quantity',))
# Now you can create your formset, bind data, etc
formset = ProductFormSetClass(request.POST)
formset.save()
More details by link:
https://docs.djangoproject.com/en/1.9/topics/forms/modelforms/#model-formsets
Also, don't forget to inspect all modelformset_factory arguments.

How do you get the correct model instance using the PK without using the URL

I am trying to pass the PK of a blog post but I am having difficulty with it. If possible I would like to pass the PK without putting it in the URL.
I am trying to let my user edit their posts which are displayed in a list like below:
I need to make sure that when the user presses edit under a post, it allows them to edit that specific post. I believe where I am going wrong is in the get_object function when I try and get the PK. Any help with this would be much appreciated!
View
class EditPost(UpdateView):
model = ProjectPost
form_class = ProjectPostForm
template_name = 'howdidu/edit_post.html'
def get_object(self):
return ProjectPost.objects.get(pk=self.request.Get.get('pk'))
def get_success_url(self):
project_username = self.request.user.username
project_slug = self.object.project.slug
return reverse('user_project', kwargs={'username':project_username, 'slug': project_slug})
model
class ProjectPost(models.Model):
project = models.ForeignKey(UserProject)
title = models.CharField(max_length=100)
post_overview = models.CharField(max_length=1000)
date_created = models.DateTimeField(auto_now_add=True)
post_views = models.IntegerField(default=0)
post_likes = models.IntegerField(default=0)
url
url(r'^edit_post/$', login_required(views.EditPost.as_view()), name='edit_post'),
link used for edit post template
Edit post

M2M using through and form with multiple checkboxes

I'd like to create a form allowing me to assign services to supplier from these models. There is no M2M relationship defined since I use a DB used by others program, so it seems not possible to change it. I might be wrong with that too.
class Service(models.Model):
name = models.CharField(max_length=30L, blank=True)
class ServiceUser(models.Model):
service = models.ForeignKey(Service, null=False, blank=False)
contact = models.ForeignKey(Contact, null=False, blank=False)
class SupplierPrice(models.Model):
service_user = models.ForeignKey('ServiceUser')
price_type = models.IntegerField(choices=PRICE_TYPES)
price = models.DecimalField(max_digits=10, decimal_places=4)
I've created this form:
class SupplierServiceForm(ModelForm):
class Meta:
services = ModelMultipleChoiceField(queryset=Service.objects.all())
model = ServiceUser
widgets = {
'service': CheckboxSelectMultiple(),
'contact': HiddenInput(),
}
Here is the view I started to work on without any success:
class SupplierServiceUpdateView(FormActionMixin, TemplateView):
def get_context_data(self, **kwargs):
supplier = Contact.objects.get(pk=self.kwargs.get('pk'))
service_user = ServiceUser.objects.filter(contact=supplier)
form = SupplierServiceForm(instance=service_user)
return {'form': form}
I have the feeling that something is wrong in the way I'm trying to do it. I have a correct form displayed but it is not instantiated with the contact and checkboxes aren't checked even if a supplier has already some entries in service_user.
You are defining services inside your Meta class. Put it outside, right after the beginning of SupplierServiceForm. At the very least it should show up then.
Edit:
I misunderstood your objective. It seems you want to show a multiple select for a field that can only have 1 value. Your service field will not be able to store the multiple services.
So, by definition, your ServiceUser can have only one Service.
If you don't want to modify the database because of other apps using it, you can create another field with a many to many relationship to Service. That could cause conflicts with other parts of your apps using the old field, but without modifying the relationship i don't see another way.
The solution to my problem was indeed to redefine my models in oder to integrate the m2m relationship that was missing, using the through argument. Then I had to adapt a form with a special init method to have all selected services displayed in checkboxes, and a special save() method to save the form using m2m relationship.
class Supplier(Contact):
services = models.ManyToManyField('Service', through='SupplierPrice')
class Service(models.Model):
name = models.CharField(max_length=30L, blank=True)
class ServiceUser(models.Model):
service = models.ForeignKey(Service, null=False, blank=False)
supplier = models.ForeignKey(Supplier, null=False, blank=False)
price = models.Decimal(max_digits=10, decimal_places=2, default=0)
And the form, adapted from the very famous post about toppings and pizza stuff.
class SupplierServiceForm(ModelForm):
class Meta:
model = Supplier
fields = ('services',)
widgets = {
'services': CheckboxSelectMultiple(),
'contact_ptr_id': HiddenInput(),
}
services = ModelMultipleChoiceField(queryset=Service.objects.all(), required=False)
def __init__(self, *args, **kwargs):
# Here kwargs should contain an instance of Supplier
if 'instance' in kwargs:
# We get the 'initial' keyword argument or initialize it
# as a dict if it didn't exist.
initial = kwargs.setdefault('initial', {})
# The widget for a ModelMultipleChoiceField expects
# a list of primary key for the selected data (checked boxes).
initial['services'] = [s.pk for s in kwargs['instance'].services.all()]
ModelForm.__init__(self, *args, **kwargs)
def save(self, commit=True):
supplier = ModelForm.save(self, False)
# Prepare a 'save_m2m' method for the form,
def save_m2m():
new_services = self.cleaned_data['services']
old_services = supplier.services.all()
for service in old_services:
if service not in new_services:
service.delete()
for service in new_services:
if service not in old_services:
SupplierPrice.objects.create(supplier=supplier, service=service)
self.save_m2m = save_m2m
# Do we need to save all changes now?
if commit:
self.save_m2m()
return supplier
This changed my first models and will make a mess in my old DB but at least it works.

Django many to many filter by ALL in list

I have the following models:
class TradeList(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, unique=True)
text = models.TextField(null=True, blank=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-modified']
def __str__(self):
return "{}'s trade list".format(self.user.username)
def get_absolute_url(self):
return reverse('tradelist_detail', kwargs={'username': self.user.username})
class TradeItem(models.Model):
tradelist = models.ForeignKey('TradeList')
card = models.ForeignKey('cards.Card')
quantity = models.PositiveSmallIntegerField()
class Meta:
abstract = True
class ForTrade(TradeItem):
class Meta:
verbose_name_plural = 'for trade'
ordering = ['card']
class LookingFor(TradeItem):
class Meta:
verbose_name_plural = 'looking for'
ordering = ['card']
I want to be able to search these models from my site, so I created a form like so:
class TradeListSearchForm(forms.Form):
for_trade = forms.ModelMultipleChoiceField(queryset=Card.objects.all())
I am using the generic FormView to handle the form, so I have overridden its form_valid method to customise the behaviour when the form has been validated successfully:
class TradeListSearchView(FormView):
form_class = TradeListSearchForm
template_name = 'trades/tradelist_search.html'
def form_valid(self, form):
search_results = TradeList.objects.filter(fortrade__card__in=form.cleaned_data['for_trade'])
return self.render_to_response(self.get_context_data(form=form,
search_results=search_results))
This works great except for one thing. At the moment, the user can select multiple cards to search for and the view searches for all tradelists that have any of those particular cards for trade. However I would like to be able to perform the search to that it looks for only those tradelists that have ALL the cards selected for trade, but I cannot think how to do this.
I think you can use a little magic:
This line is what kills your boom:
search_results = TradeList.objects.filter(fortrade__card__in=form.cleaned_data['for_trade'])
Instead of it, use this: (notice i'm explicitly filtering down the TradeList instances many times)
for_trade = form.cleaned_data['for_trade']
search_results = Tradelist.objects.all()
for card in for_trade:
search_results = search_results.filter(fortrade__card__id=card.id)
This way, you'll filter your objects on all for all of the cards. Previously you were returning (as you already said) every tradelist that had a card among your selection.