Django how to set initial object to ModelList? - python

I've created a simple model
class Person(models.Model):
name = models.CharField(max_length=25)
surname = models.CharField(max_length=25)
birth_date = models.DateField()
...
and a simple ModelForm
class PersonForm(ModelForm):
class Meta:
model = Person
how can I put a Person object that I already have (e.g. Person.objects.get(pk=1)) to become an initial value for the form? I need to edit this object

From the docs:
# Creating a form to add an article.
>>> form = ArticleForm()
# Creating a form to change an existing article.
>>> article = Article.objects.get(pk=1)
>>> form = ArticleForm(instance=article)
so in your case, in your views.py:
person = Person.objects.get(pk=1)
form = PersonForm(instance=person)
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#modelform

Related

Three-level inline formsets in the frontend

I'm trying to accomplish a three-level stacked inline form in Django. Suppose these models:
class Anuncio(models.Model):
title = models.CharField(max_length=200)
delivery = models.CharField(max_length=100)
class Product(models.Model):
anuncio = models.ForeignKey(Anuncio, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
quantity = models.PositiveIntegerField(default=1)
price = models.PositiveIntegerField()
class Image(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
image = models.ImageField()
There is a relation Anuncio-Product and another relation Product-Image. With this Django package, I accomplished exactly what I want in the Django admin: when creating an Anuncio object, I can add as many Products as I want, and those products can have as many Images as I want. I'm trying to accomplish this in the front end.
I think the way to go is with Django formsets, but I'm facing some problems. All the resources I've been able to find online are only 'two-level' formsets or in 'three-level' cases all the foreign keys point to the same parent model.
With this forms.py file:
class ProductForm(ModelForm):
class Meta:
model = Product
fields = ['name', 'quantity', 'price']
class ImageForm(ModelForm):
class Meta:
model = Imagen
fields = ['image']
class AnuncioForm(ModelForm):
class Meta:
model = Anuncio
fields = ['title', 'delivery']
And this views.py function:
def anunciocreateview(request):
form = AnuncioForm(request.POST or None)
ProductFormSet = inlineformset_factory(Anuncio, Product, form=ProductForm)
ImageFormSet = inlineformset_factory(Product, Image, form=ImageForm)
if all([form.is_valid(), ProductFormSet.is_valid(), ImageFormSet.is_valid()]):
parent = form.save(commit=False)
parent.anunciante = request.user
parent.save()
for form in ProductoFormSet:
child = form.save(commit=False)
child.anuncio = parent
child.save()
for form in ImagenFormSet:
imagen = form.save(commit=False)
imagen.product = form.product
imagen.save()
context = {
'form_1' : form,
'form_2' : ProductFormSet,
'form_3' : ImageFormSet,
}
But I think I'm missing important points when it comes to add the proper relations between models. This set-up gives an AttributeError of: 'ProductForm' object has no attribute '__name__'
The, for example, 'add (extra) Product' that appears in AdminStackedInLine I guess it can be accomplished with JavaScript, playing with hidden forms and changing attributes on click events.
Anyone has experience doing something similar or can guide me through the correct direction? Also on how to manage the data and the relations of the submitted forms?
I think your problem is you have tried to validate a class Form instead of instanciate your formset and validate them.
Your code would be look like to something like that :
def anunciocreateview(request):
ProductFormSet = inlineformset_factory(Anuncio, Product, form=ProductForm)
ImageFormSet = inlineformset_factory(Product, Image, form=ImageForm)
anuncio_form = AnuncioForm(request.POST or None)
product_formset = ProductFormSet(request.POST or None)
image_formset = ImageFormSet(request.POST or None)
if all([form.is_valid(), product_formset.is_valid(), image_formset.is_valid()]):
...
The function inlineformset_factory just create a Form class, not a instance of form.
More information and example on the documentation : https://docs.djangoproject.com/fr/4.1/topics/forms/formsets/

Django form custom field

If we have a modelForm with some fields not directly corresponding to the model, how do we have the form process them in a custom way, while saving the rest of fields as by default?
For example, we have a model for an item that supports multilingual descriptions. The models are:
class Item(models.Model):
name = models.ForeignKey(Localization)
on_sale = models.BooleanField(default=False)
class Localization(models.Model):
de = models.TextField(blank=True, null=True, verbose_name='de')
eng = models.TextField(blank=True, null=True, verbose_name='eng')
The form to add/edit an Item looks like that:
class ItemForm(forms.ModelForm):
id = forms.CharField(widget=forms.HiddenInput(), max_length=128, label='')
name_eng = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='eng')
name_de = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='de')
on_sale = forms.CharField(widget=forms.CheckboxInput(), label='on_sale', )
class Meta:
model = Item
fields = ('id', 'on_sale',)
Now what saving this form should do, is for a new Item - create Localization object with the two name fields, then create an Item object with on_sale field, linked to Localization object. For an existing Item - edit the corresponding Localization object and then on_sale field of the Item itself.
I did the task with a separate function, that processes the custom fields from the request separately, but having it all done by the form's save() method looks better. Or am I wrong?
PS I'm sorry to be asking an evidently worn question, but I simply fail to do what I want with other examples.
Update:
I actually got it working the way I wanted with the help of the hints from here. Code goes as that, please let me know if it can be optimized.
class NameForm(forms.ModelForm):
# id = forms.CharField(widget=forms.HiddenInput(), max_length=128, label='')
id = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='id', required=False)
name_eng = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='eng')
name_de = forms.CharField(widget=forms.TextInput(attrs={'style': 'width:200px;'}), label='de')
gender = forms.CharField(widget=forms.CheckboxInput(), label='gender', required=False)
class Meta:
model = Name
fields = ('id', 'gender',)
def save(self):
instance = super(NameForm, self).save(commit=False)
obj_id = self.cleaned_data['id']
if obj_id:
instance_bd = Name.objects.get(pk=obj_id)
loc = instance_bd.name
loc.de = self.cleaned_data['name_de']
loc.eng = self.cleaned_data['name_eng']
loc.save()
instance.id = obj_id
else:
loc = Localization(de=self.cleaned_data['name_de'], eng=self.cleaned_data['name_eng'])
loc.save()
instance.name = loc
instance.save()
return instance
The view is simply
#login_required
def admin_lists(request):
if request.method == 'POST':
form = NameForm(request.POST)
if form.is_valid():
form.save()
forms = {'name': NameForm()}
return render(request, 'admin/lists.html', {'forms': forms})
In this case is better don't use the forms'save method, try this
if request.method == 'POST':
form = ItemForm(request.POST)
# check whether it's valid:
if form.is_valid():
post = request.POST
set_name_eng= post['name_eng']
set_name_de= post['name_de']
set_on_sale = post['on_sale']
#now here we create the anothers objects
a = Localization(de=set_name_de, eng=set_name_eng)
a.save()
b = Item(name=a, on_sale=sale)
b.save()
I didn't understand the last part, but I think you need to organize better your models and forms, Let me know and I'll try to help you

Django: 'Thoughts' object has no attribute 'choice_set' - Trouble accessing a choice set

In my model I define a Thoughts and Comments model. One thought has many Comments as so:
class Thoughts(models.Model):
name = models.CharField(max_length=30)
thought = models.CharField(max_length=500)
class Comments(models.Model):
name = models.CharField(max_length=30)
comment = models.CharField(max_length=200)
original_post = models.ForeignKey(Thoughts, default=0)
On my site, when you go to view a thought, I want all of the comments to appear. It is my understanding that you can use choice_set to access attributes via one-to-many relationship. Here's my view:
def thought(request, thought_num):
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
c = Comments.objects.create(name=form.cleaned_data['name'],
comment=form.cleaned_data['comment'])
c.save()
else:
form = CommentForm()
get_post = Thoughts.objects.get(pk=thought_num)
comments = get_post.choice_set.all()
return render(request, 'thought.html', {'form': form, 'comment':comments,})
In these lines, I attempt to access all comments related to a particular thought in order to print them in my template.
get_post = Thoughts.objects.get(pk=thought_num)
comments = get_post.choice_set.all()
When I access the page that should display the comments, I get this error:
Exception Type: AttributeError
Exception Value:'Thoughts' object has no attribute 'choice_set'
Perhaps I am missing something, I am not sure. I'm sure it's something simple. Thanks for your time
To retrieve all the Comments related to a Thought. You can do the following:
Thoughts.objects.get(pk=thought_num).comments_set.all()
If you would like to override the default related_name ("comments_set"). You can do the following:
original_post = models.ForeignKey(Thoughts, default=0, related_name='choice_set')
When you make a ForeignKey the default related name becomes the lower case name of the current class + "_set" so for your project should be:
get_post = Thoughts.objects.get(pk=thought_num)
comments = get_post.comments_set.all()
Or you could even create a custom related name instead of the default:
class Thoughts(models.Model):
name = models.CharField(max_length=30)
thought = models.CharField(max_length=500)
class Thoughts(models.Model):
name = models.CharField(max_length=30)
thought = models.CharField(max_length=500)
class Comments(models.Model):
name = models.CharField(max_length=30)
comment = models.CharField(max_length=200)
original_post = models.ForeignKey(Thoughts, default=0, related_name='comments')
so you can get your comments like this:
get_post = Thoughts.objects.get(pk=thought_num)
comments = get_post.comments.all()

how to save Many to Many relationship in django

How to create an object for a Django model with a many to many field?
From above question i come to know we can save Many to Many field later only.
models.py
class Store(models.Model):
name = models.CharField(max_length=100)
class Foo(models.Model):
file = models.FileField(upload_to='')
store = models.ManyToManyField(Store, null=True, blank=True)
views.py
new_track.file = request.FILES['file']
new_track.save()
And file uploading working fine then later i modify my code to add store then i am here...
Now i am sure db return id's here. Then i tried with my below code but that's given me error only
x = new_track.id
new = Foo.objects.filter(id=x)
new.store.id = request.POST['store']
new.save()
ok so the error here is 'QuerySet' object has no attribute 'store'
And also i tried with add that's now working either.
So the question is how to save()
the right way of saving objects with manytomany relations would be:
...
new_track.file = request.FILES['file']
new_track.save()
new_store = Store.objects.get(id=int(request.POST['store']))
new_track.store.add(new_store)
As of 2020, here's my approach to saving ManyToMany Field to a given object.
Short Answer
class HostingRequestView(View):
def post(self, request, *args, **kwargs):
form = VideoGameForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.updated_by = request.user
obj.save()
selected_categories = form.cleaned_data.get('category') #returns list of all selected categories e.g. ['Sports','Adventure']
#Now saving the ManyToManyField, can only work after saving the form
for title in selected_categories:
category_obj = Category.objects.get(title=title) #get object by title i.e I declared unique for title under Category model
obj.category.add(category_obj) #now add each category object to the saved form object
return redirect('confirmation', id=obj.pk)
Full Answer
models.py
class Category(models.Model):
title = models.CharField(max_length=100, null=True, unique=True)
class VideoGame(models.Model):
game_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50, blank=False, null=False)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=False, on_delete=models.CASCADE)
category = models.ManyToManyField(Category) #ManyToMany Category field
date_added = models.DateTimeField(auto_now_add=True, verbose_name="date added")
forms.py ModelForm
class VideoGameForm(forms.ModelForm):
CATEGORIES = (
('Detective', 'Detective'),
('Sports', 'Sports'),
('Action', 'Action'),
('Adventure', 'Adventure'),
)
category = forms.MultipleChoiceField(choices=CATEGORIES, widget=forms.SelectMultiple())
class Meta:
model = VideoGame
fields = ['name', 'category', 'date_added']
views.py on POST
class HostingRequestView(View):
def post(self, request, *args, **kwargs):
form = VideoGameForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.updated_by = request.user
obj.save()
selected_categories = form.cleaned_data.get('category') #returns list of all selected categories e.g. ['Sports','Adventure']
#Now saving the ManyToManyField, can only work after saving the form
for title in selected_categories:
category_obj = Category.objects.get(title=title) #get object by title i.e I declared unique for title under Category model
obj.category.add(category_obj) #now add each category object to the saved form object
return redirect('confirmation', id=obj.pk)
URL path for redirect
urlpatterns = [
path('confirmation/<int:id>/', Confirmation.as_view(), name='confirmation'),
]
I hope this can be helpful. Regards
new.stores.all()
returns all stores linked to the object.
Maybe:
Change Foo to Tracks
Tracks.objects.filter(id=x) to Tracks.objects.get(id=x)
Let me know how it goes
why this confusion so much.. you are getting the id there then, call the store like
new_track.save()
new_track.store.add(request.POST['store'])

Copying over the fields from a ModelForm into a Form

I have a dummy Form that looks like:
class MyForm(forms.Form):
class __init__(self, *args, **kwargs):
pass
Here's are the ModelForms and their respective Models:
class Person(models.Model):
fname = models.CharField(max_length = 255)
lname = models.CharField(max_length = 255)
class Address(models.Model):
address = models.CharField(max_length = 255)
person = models.ForeignKey(Person)
class PersonForm(ModelForm):
class Meta:
model = Person
class AddressForm(ModelForm):
class Meta:
model = Address
I would like to initiate MyForm like this myfrm = MyForm(PersonForm, AddressForm).
How can I dynamically add the fields of each of the Forms to MyForm excluding AutoFields and ForeignKey fields i.e. MyForm when initiated would have three fields in the end — fname, lname and address. It's a simple copying of the fields from one form to another but I'm a little lost with it.
Thanks
Just adding a field is not correct, considering the models above.
Address has a ManyToOne relation to Person - A user can have multiple addresses. In that case better take a look at django's inline form sets:
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#using-an-inline-formset-in-a-view
Cumbersome, but You can also manually add a CharField to to Person's ModelForm, override it's save and handle saving the content of that field to Address (ugly, I don't like it).
You can put both forms inside one html <form> tag, using the prefix argument. You then do a bit of work in the view to link the address to the person.
def my_view(request):
if request.method == "POST":
person_form = PersonForm(data=request.POST, prefix="person")
address_form = AddressForm(data=request.POST, prefix="address")
if person_form.is_valid() and address_form.is_valid():
person = person.save()
address = address.save(commit=False)
address.person = person
address.save()
return HttpResponseRedirect('/success-url/')
...

Categories