Django - Createview form_valid object.id error - django

I'm trying to create a form where the object created (a project) has a relationship with another model (the channel). The problem is I can't workout how to call the channel's primary key for the project's relationship.
Models.py:
class Project(models.Model):
channel = models.ForeignKey(
'Channel',
on_delete=models.CASCADE,
)
Views.py:
class ProjectCreate(CreateView):
model = Project
fields = ['name', 'description']
def form_valid(self, form):
Project = form.save(commit=False)
form.instance.channel = Channel.objects.get(id=self.kwargs['channel'])
Project.channel = channel
return super(ProjectCreate, self).form_valid(form)
I think something else needs to be added to the forms.py file as well:
Forms.py:
class ProjectForm(forms.Form):
name = forms.CharField(max_length=50)
description = forms.CharField(widget=forms.Textarea)

Firstly, you should use a ModelForm so that you can save it to create the instance. Don't include channel in the fields, because you're going to set it in the view.
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = ['name', 'description']
Then, assuming that your url pattern is correctly configured to include the channel, all you need to do is set the channel on the form instance and call the parent class' form_valid method.
def form_valid(self, form):
form.instance.channel = Channel.objects.get(id=self.kwargs['channel'])
return super(ProjectCreate, self).form_valid(form)

Related

Django 3.0.8 Field.disable UpdateView

For example I have a EntryUpdateForm that inherits from UpdateView that updates some data on a model. Now I'd like to disable certain fields. From the Django documentiation I'm not sure where exatcly to put the Field.disable attribute to disable a certain field on a form.
forms.py
class EntryUpdateForm(UpdateView):
class Meta:
model = Entry
fields = ["material", "shape", "diameter", "taken_from", "moved_to", "quantity"]
views.py
class EntryUpdateView(LoginRequiredMixin, EntryUpdateForm):
model = Entry
fields = ["material", "shape", "diameter", "taken_from", "moved_to", "quantity"]
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
I'm not even sure if it's possible to do this if I'm inheriting from the UpdateView, at least easly.
class EntryUpdateForm(UpdateView):
material = forms.CharField(widget=forms.TextInput(attrs={'readonly':'True'}))
class Meta:
model = Entry
fields = ["material", "shape", "diameter", "taken_from", "moved_to", "quantity"]
Replace material with whichever field you want to disable and use the appropriate widget
Thanks to a comment from mursalin I managed to get it to work.
forms.py
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ["material", "shape", "diameter", "taken_from", "moved_to", "quantity"]
material = forms.ChoiceField(disabled=True)
shape = forms.ChoiceField(disabled=True)
diameter= forms.CharField(disabled=True) # add whichever field you want to disable
class EntryUpdateForm(UpdateView):
class Meta:
model = Entry
views.py
class EntryUpdateView(LoginRequiredMixin, EntryUpdateForm):
model = Entry
form_class = EntryForm # pass the form in the view to get it to change
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)

django fill form field automatically from context data

I have a form attached to a DetailedView and its working fine when saved. I would like the form field(position) to be prepopulated with the value coming from the slug of the detailed view(e.g jobs/human-resource-manager). The Model of the form field has a Foreignkey to the JobPost model. Need help. Part of my view looks like this
class JobsDetailView(DetailView):
model = JobPost
template_name = 'job_post-detail.html'
def get_context_data(self, **kwargs):
context = super(JobsDetailView, self).get_context_data(**kwargs)
context['position'] = JobPost.objects.order_by('position')
context['job_app_form'] = JobsForm()
return context
foms.py
from django import forms
from job_post.models import JobsApplied
class JobsForm(forms.ModelForm):
class Meta:
model = JobsApplied
fields = '__all__'
def form_valid(self, form):
form.instance.customuser = self.request.user
return super().form_valid(form)
I'm assuming you do not want your users to be able to interact with or change these prefilled values.
I'm making a comments/review model and I want it to automatically link reviews to the people they are about
models.py
class Review(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
...
I hide the person field in the ReviewsForm to prevent user input by either omitting it from the 'fields' or adding it to an 'exclude'.
forms.py
class ReviewsForm(forms.ModelForm):
class Meta:
model = Review
fields = ('rating', 'summary', 'review_text')
Then, when processing the form in the view, I use commit=False so I can manipulate field values before saving to the database.
Include prefilled values, save and then redirect the user wherever is ideal
views.py
def person(request, area_slug, category_slug, person_id):
...
if form.is_valid():
pending_review = form.save(commit=False)
pending_review.person = Person.objects.get(pk = person_id)
pending_review.save()
return HttpResponseRedirect(...)
django fill form field automatically from context data for django form and django formsets
For formsets in forms.py
StoreRequestAccessoryUpdateFormSet = forms.modelformset_factory(StoreRequestAccessory, form=StoreRequestAccessoryUpdateForm, exclude=["storeRequestId"], can_delete=True)
In get_context_data you can add it as you like for django
class StoreRequestUpdateView(LoginRequiredMixin, UpdateView):
template_name = "Inventory/Stock/StoreRequest/StoreRequestUpdateView.html"
model = StoreRequest
fields = ["fromStoreId", "toStoreId", "reference", "status", "remark"]
def get_context_data(self, **kwargs):
context = super(StoreRequestUpdateView, self).get_context_data(**kwargs)
print(self.object.pk)
context.update({
# "StoreRequestForm": context.get("form"),
"StoreRequestForm": StoreRequestUpdateForm(instance=StoreRequest.objects.get(id=self.object.pk)),
"StoreRequestAccessoryForm": StoreRequestAccessoryUpdateFormSet(
queryset=StoreRequestAccessory.objects.filter(storeRequestId=self.object.pk),
prefix="storereq_accessory_form"),
})
return context

How can I upload multiple files to a model field?

I want to upload multiple files through a ModelForm,with all files to be assigned to a file field of the Model.I have gone through the docs and I saw an example on it and I ve implemented it here but I can only get my form to pick multiple files but only one get saved and assigned to filesfield.Below are my codes
models.py
class Feed(models.Model):
user=models.ForeignKey(User,on_delete=models.CASCADE,related_name='feeds')
text=models.TextField(blank=False,max_length=500)
files = models.FileField(upload_to="files/%Y/%m/%d")
forms.py
class FeedForm(ModelForm):
class Meta:
model=Feed
fields=('text','auth','files')
widgets={"files":forms.FileInput(attrs={'id':'files','required':True,'multiple':True})}
and views.py
def post_feed(request):
form_class = FeedForm
if request.method == 'POST':
form = form_class(request.POST,request.FILES)
if form.is_valid():
feed = form.save(commit=False)
feed.user = User.objects.get(pk=1)
feed.pub_date=timezone.now()
#instance = Feed(files=request.FILES['files'])
# feed.files=request.FILES['files']
feed.save()
return redirect('home')
else:
form = form_class()
return render(request, 'post_feed.html', {'form': form,})
from django.views.generic.edit import FormView
from .forms import FeedForm
class FileFieldView(FormView):
form_class=FeedForm
template_name='post_feed.html'
'''success_url=??? #I dont know what to write here.I thought of putting this
render(request, 'post_feed.html', {'form': form,}) because I just want
to reload the page but it gave an error,so I removed it entirely.'''
def post_feed(self,request,*args,**kwargs):
form_class=self.get_form_class()
form=self.get_form(form_class)
filez=request.FILES.getlist('files')
if form.is_valid():
for f in filez:
f.save()
return self.form_valid(form)
else:
return self.form_invalid(form)
Kindly help me out,Thanks in advance.
You have to create a separate model for the files and connect them with a foreign key:
class Feed(models.Model):
user=models.ForeignKey(User, on_delete=models.CASCADE, related_name='feeds')
text=models.TextField(blank=False, max_length=500)
class FeedFile(models.Model):
file = models.FileField(upload_to="files/%Y/%m/%d")
feed = models.ForeignKey(Feed, on_delete=models.CASCADE, related_name='files')
I hope this helps.
Phew, it took me a whole day to figure out this. My goal was to assign multiple files to one instance of a class, like a Blog instance can have multiple Images. First things first, you cannot do this with one models.FileField inside a model (for example inside Blog class), because this field was not designed to save multiple files. So the solution is to create separate model for the files and connect them with One-to-Many Relationship (Foreign Key) as it was answered by #Carlos Mermingas.
Enough words, here is the code for the above situation:
# models.py
class Feed(models.Model):
user=models.ForeignKey(User, on_delete=models.CASCADE)
text=models.TextField(blank=False, max_length=500)
class FeedFile(models.Model):
file = models.FileField(upload_to="files/%Y/%m/%d")
feed = models.ForeignKey(Feed, on_delete=models.CASCADE)
# forms.py
...
from django.forms import ClearableFileInput
...
class FeedModelForm(forms.ModelForm):
class Meta:
model = Feed
fields = ['text']
class FileModelForm(forms.ModelForm):
class Meta:
model = FeedFile
fields = ['file']
widgets = {
'file': ClearableFileInput(attrs={'multiple': True}),
}
# widget is important to upload multiple files
# views.py
from .models import FeedFile
...
def create_to_feed(request):
user = request.user
if request.method == 'POST':
form = FeedModelForm(request.POST)
file_form = FileModelForm(request.POST, request.FILES)
files = request.FILES.getlist('file') #field name in model
if form.is_valid() and file_form.is_valid():
feed_instance = form.save(commit=False)
feed_instance.user = user
feed_instance.save()
for f in files:
file_instance = FeedFile(file=f, feed=feed_instance)
file_instance.save()
else:
form = FeedModelForm()
file_form = FileModelForm()
# the rest is the basic code: template_name, context, render etc.
# in your template.html <form> tag must include enctype="multipart/form-data"
Bonus: if you want to see uploaded files in admin panel, you can use InlineModelAdmin objects. Here is the code:
# admin.py of your app
from django.contrib import admin
from .models import Feed, FeedFile
class FeedFileInline(admin.TabularInline):
model = FeedFile
class FeedAdmin(admin.ModelAdmin):
inlines = [
FeedFileInline,
]
admin.site.register(Feed, FeedAdmin)
For the more details on file upload, Model Forms, how to include widget in Model Form
Would suggest using an M2M field from Feed model to FeedFile model.
Makes it all the more easier while querying for files of a particular Feed object, which i feel is also the most common usecase for Feed objects
class Feed(models.Model):
user=models.ForeignKey(User, on_delete=models.CASCADE, related_name='feeds')
text=models.TextField(blank=False, max_length=500)
files=models.ManyToManyField(FeedFile)
class FeedFile(models.Model):
file = models.FileField(upload_to="files/%Y/%m/%d")

what does the autocomplete-light return?

I have recently installed autocomplete-light in my app.
Autocomplete filters through the field called 'name' in a table called institution. However, what is post through the view is the 'id' of the same object, not the name.
Does anyone know why that is?
My view is:
class UserAccountsUpdate(UpdateView):
context_object_name = 'variable_used_in `add_user_accounts.html`'
form_class = AddUserAccountsForm
template_name = 'add_user_accounts.html'
success_url = 'add_user_accounts.html'
def add_user_institution_details(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = AddUserAccountsForm(request.POST)
# check whether it's valid:
if form.is_valid():
institution_selected = Institution.objects.get(id=name)
form.save()
return render(request)
#get object
def get_object(self, queryset=None):
return self.request.user
The form is:
class AddUserAccountsForm(forms.ModelForm):
name = forms.ModelChoiceField(required=True, queryset=Institution.objects.all(), widget=autocomplete_light.ChoiceWidget('InstitutionAutocomplete'), label="")
class Meta:
model = Institution
fields = ('name',)
autocomplete-light's ChoiceWidget uses the Model's PrimaryKey for post requests by default, which in your case is id.
Since you did not post your models.py I can only assume that name is a CharField in the Institution model and you are just using autocomplete here to simplify the adding of a name.
To realize this use TextWidget and forms.CharField:
class AddUserAccountsForm(forms.ModelForm):
name = forms.CharField(
required=True,
widget=autocomplete_light.TextWidget('InstitutionAutocomplete'),
label="",
)
class Meta:
model = Institution
fields = ('name',)

How to save user or session data in FormView along with form data?

I am trying to develop a small app a user can upload images after logging in. I have used ModelForm to generate the form for uploading the image and its decription. The ArtWork model has foreign key relationship with the User model. I exclude the user field from the ModelForm. Now I do not have any clue how to save the logged in user along with the form data into the database. Thanks in advance.
Here is my ArtWork Model
class Artwork(models.Model):
art_image = models.ImageField("Art Image", upload_to="images", blank=True,null=True)
art_name = models.CharField(max_length=100, blank=True,null=True)
user = models.ForeignKey(AuthUser, default=1)
def __unicode__(self):
return self.art_name
Here is my View
class UploadArtworkView(FormView):
form_class = ArtworkForm
success_url = "/"
template_name = "artwork/upload.html"
def form_valid(self, form):
#artwork = Artwork(art_image= self.get_form_kwargs().get('files')['art_image'])
#print "Art name is" + form.post['art_name']
form.save()
print self.request.user.name
return super(UploadArtworkView,self).form_valid(form)
Here is the Form
from django.forms import ModelForm
from .models import Artwork
class ArtworkForm(ModelForm):
class Meta:
model = Artwork
fields = ['art_image','art_name']
Your use case is very similar to this example in the Django docs. You have already realised that form_valid is the correct method to override, so you are very close to the answer. The trick is to access the object using self.form.instance, and set the user:
def form_valid(self, form):
form.instance.user = self.request.user
return super(UploadArtworkView,self).form_valid(form)