I have two django models which are :
class Dataset(models.Model):
name = models.CharField(max_length = 200)
description = models.CharField(max_length=1000)
owner = models.ForeignKey(Profile, null=True, on_delete=models.SET_NULL)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Source(models.Model):
name = models.CharField(max_length = 200)
description = models.CharField(max_length=1000)
dataset = models.ForeignKey(Dataset, null=True, on_delete=models.SET_NULL)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
When saving a Source, I would like to initiate the value of the corresponding Dataset. I tried to initiate the value of my form as suggested here : foreign key as initial value not passed to the ModelForm in django
def create_source(request, dataset_id):
user = request.user
dataset = Dataset.objects.get(id=dataset_id)
form = SourceForm(initial={"dataset" : dataset, })
if request.method == "POST":
form = SourceForm(request.POST or None, initial={"dataset" : dataset, })
if form.is_valid():
source = form.save()
# dataset.source_set.add(source) # Only works if I add this line
return redirect("source", dataset_id=dataset_id, source_id=source.id)
context = {"form": form}
return render(request, "sources/source_form.html", context)
The SourceForm:
class SourceForm(ModelForm):
class Meta:
model = Source
fields = "__all__"
exclude = ["dataset"]
The suggested way does not work. I was able to achieve the desired result by adding the commented line above. It is not a recommended solution since it makes a second call to the database.
Any idea how to give properly the dataset object to the source ?
Passing values in initial for excluded fields does not do anything. Instead what you can do is modify the instance wrapped by the form before saving the it:
def create_source(request, dataset_id):
user = request.user
dataset = Dataset.objects.get(id=dataset_id)
form = SourceForm() # No initial
if request.method == "POST":
form = SourceForm(request.POST) # this is a submitted form `request.POST or None` makes no sense use only `request.POST`
if form.is_valid():
form.instance.dataset = dataset
source = form.save()
return redirect("source", dataset_id=dataset_id, source_id=source.id)
context = {"form": form}
return render(request, "sources/source_form.html", context)
Related
I have a view where the user inserts a number of units and once submit button is clicked the excel file is exported as xlsx file. I am using Django import-export to do that BUT I don't know how to filter the CERequests model so the user sees only what (s)he has just inserted. I have implemented the filtering by the user but when clicking submits button it filters all items by the current user but it shows all of them (also items from the past).
What I want is to export only values from the current formset or formsets. What I tried is to put created=created in filter method but it gives me an empty Excel file. When I remove it gives me a list of all CERquests that the user inserted.
What do I need to do to get only data from the current formset(s)?
views.py
class CostCalculator(LoginRequiredMixin, TemplateView):
template_name = 'calculator/cost_calculator.html'
def get(self, *args, **kwargs):
# Create an instance of the formset
formset = CalculatorForm(initial=[{
'author': self.request.user.email,
}])
return self.render_to_response({'ce_request_formset': formset})
# Define method to handle POST request
def post(self, *args, **kwargs):
formset = CalculatorForm(data=self.request.POST)
# Check if submitted forms are valid
if formset.is_valid():
for form in formset:
related_product = form.cleaned_data.get('related_product')
created = form.cleaned_data.get('created')
form.save()
qs = CERequest.objects.filter(related_product__title=related_product, created=created)
dataset = CERequestResource().export(qs)
response = HttpResponse(dataset.xlsx, content_type="xlsx")
response['Content-Disposition'] = 'attachment; filename=filename.xlsx'
return response
return self.render_to_response({'ce_request_formset': formset})
forms.py
class CalculatorForm(forms.ModelForm):
author = forms.CharField(required = False)
number_of_units = forms.IntegerField(help_text='Only numeric values are allowed.', min_value=0)
total_price = forms.IntegerField(widget = forms.HiddenInput(), required = False)
created = forms.DateTimeField(widget = forms.HiddenInput(), required = False)
class Meta:
model = CERequest
fields = ('author', 'related_product', 'related_component', 'number_of_units', 'total_price')
readonly_fields = ('created')
CalculatorForm = formset_factory(CalculatorForm)
models.py
class CERequest(models.Model):
author = models.CharField(max_length=255, blank=True, null=True)
related_component = models.ForeignKey(CostCalculator, on_delete=models.CASCADE, blank=True, null=True)
number_of_units = models.IntegerField(default=0)
related_product = models.ForeignKey(Product, on_delete=models.CASCADE, null=True, blank=True)
created = models.DateTimeField(auto_now=True)
total_price = models.IntegerField(default=0, blank=True, null=True)
I am trying to assign username to author field in Post model , Django spews out the following error:
"Post.author" must be a "User" instance.
model:
class Post(models.Model):
title = models.CharField(max_length=200)
image = models.ImageField(upload_to='',null=True,blank=True)
image_url = models.CharField(max_length=200,default=None,null=True,blank=True)
date = models.DateTimeField(default=timezone.now)
content = models.TextField()
author = models.ForeignKey(User, null=False, blank=False,on_delete=models.CASCADE)
categories = models.ManyToManyField(Category)
published = models.BooleanField()
def __str__(self):
return self.title
view:
#login_required
def new_post(request):
# Add a new post
if request.method != 'POST':
# No data submitted, create a blank form
form = PostForm()
else:
# Post data submitted, process data
form = PostForm(data=request.POST)
if form.is_valid():
new_post = form.save(commit=False)
new_post.author = request.user.username
new_post.save()
return redirect('elogs:posts')
#Display a blank or invalid form
context = {'form':form}
return render(request,'elogs/new_post.html',context)
form:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title','content','image','image_url','published']
widgets = {
'title': forms.Textarea(attrs={'placeholder': 'Title..'}),
'content': forms.Textarea(attrs={'placeholder': 'What is on your mind?'}),
'categories': forms.TextInput()
}
I have solved this error just like this:
from django.contrib.auth import get_user_model
author = models.OneToOneField(get_user_model(),on_delete=models.CASCADE)
You're giving the username instead of the user itself:
new_post.author = request.user.username
A ForeignKey takes an instance of the given Model, User in this case.
I am having a little problem with Django. When I try to access a particular view I get the error of the title __ str__ returned non-string (type DeferredAttribute). I believe it has something to do with a form that I render in the view but I can't find the problem by myself.
Here is my model:
class ItemElements(models.Model):
itemid = models.ForeignKey(Items, on_delete = models.CASCADE)
elementid = models.ForeignKey(Elements, on_delete = models.CASCADE)
percentage = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
The form:
class ItemElementsForm(forms.ModelForm):
class Meta:
model = ItemElements
fields = ['itemid', 'elementid', 'percentage']
labels = {
'itemid' : 'Item',
'elementid' : 'Element',
'percentage' : 'Percentage'
}
The view:
def addelements(request):
if request.method == 'POST':
form = ItemElementsForm(request.POST)
if form.is_valid():
elementsForm= form.save(commit=False)
elementsForm.save()
return redirect('addelements')
else:
form = ItemElementsForm()
return render(
request,
'add/addelements/index.html',
{'form': form}
)
I can't really see the problem. I copied and modified from another working form and view. Eveything else it's exactly the same as the other form that just works.
Check your models. You should have something like
class ItemElements(models.Model):
itemid = models.ForeignKey(Items, on_delete = models.CASCADE)
elementid = models.ForeignKey(Elements, on_delete = models.CASCADE)
percentage = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self): #For Python 3, use __unicode__ on Python 2
return str(self.created_at)
Well, apparently I found the problem. All I had to do was to create a primary key on the model.
primaryid = models.AutoField(primary_key = True)
models.py
class Playlist(models.Model):
name = models.CharField(max_length=50)
num_of_songs = models.IntegerField(max_length=15)
duration = models.IntegerField()
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)
songs = models.ManyToManyField("Song", blank=True)
forms.py
class PlaylistEditForm(forms.ModelForm):
class Meta:
model = Playlist
fields = ['name', 'songs']
I calculate duration and num_of_songs based on the songs I get from the form. But I do that calculation from a view.
views.py
playlist = Playlist.objects.get(id=playlist_id)
if request.method == 'POST':
form = PlaylistEditForm(request.POST, instance=playlist)
if form.is_valid():
form.save()
playlist.duration = playlist.songs.aggregate(Sum('duration'))['duration__sum'] or 0
playlist.num_of_songs = playlist.songs.count()
playlist.save()
I want to calculate duration and num_of_songs inside a form.
you can move the calculation to the form overriding the form's save method
def save(self, commit=True):
instance = super(PlaylistEditForm, self).save(commit=False)
instance.duration = instance.songs.aggregate(Sum('duration'))['duration__sum'] or 0
instance.num_of_songs = instance.songs.count()
if commit:
instance.save()
return instance
and your view becomes
playlist = Playlist.objects.get(id=playlist_id)
if request.method == 'POST':
form = PlaylistEditForm(request.POST, instance=playlist)
if form.is_valid():
form.save()
Please refer to Django's official docs for further info.
Is there any reason not to do it in the models save() method? If you have it in the forms save() method, the duration and num_of_songs in the database will not get updated if you save the model instance other than from the modelform.
class Playlist(models.Model):
name = models.CharField(max_length=50)
num_of_songs = models.IntegerField(max_length=15)
duration = models.IntegerField()
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)
songs = models.ManyToManyField("Song", blank=True)
def save(self, *args, **kwargs):
if self.pk:
self.duration = self.songs.aggregate(Sum('duration'))['duration__sum'] or 0
self.num_of_songs = self.songs.count()
return super(Playlist, self).save(*args, **kwargs)
You view would be:
if request.method == 'POST':
form = PlaylistEditForm(request.POST, instance=playlist)
if form.is_valid():
playlist = form.save()
I have this going on in one of my class based views:
if form.is_valid():
form.save(commit=False)
form.manager = User.objects.get(username=request.user.username)
form.save()
location = Location.objects.get(name=form.cleaned_data['name'])
user.get_profile().owned_locations.add(location)
return redirect(reverse('location_manager'))
Yet when I fill out the form, I get the following error:
IntegrityError at /dash/location/add
locationmanager_location.manager_id may not be NULL
This is strange given my models for Location look like this:
class Location(models.Model):
region = models.ForeignKey(Region)
manager = models.ForeignKey(User)
name = models.CharField(max_length=255)
street_address = models.TextField(blank=True)
city = models.CharField(max_length=255, blank=True)
zip_code = models.CharField(max_length=20, blank=True)
And my Forms.py looks like this:
class locationForm(forms.ModelForm):
class Meta:
model = Location
fields = (
'region',
'name',
'street_address',
'city',
'zip_code',
)
exclude =('manager',)
I cannot seem to figure out what the issue is here. If you are curious why I have the exclude, I followed some advice from Daniel Roseman here
Instead of :
form.save(commit=False)
form.manager = User.objects.get(username=request.user.username)
form.save()
try:
obj = form.save(commit=False)
obj.manager = User.objects.get(username=request.user.username)
obj.save()
Hope these helps!
Your form handling is slightly incorrect, you should do it like this:
if form.is_valid():
instance = form.save(commit=False)
instance.manager = User.objects.get(username=request.user.username)
instance.save()
location = Location.objects.get(name=form.cleaned_data['name'])
user.get_profile().owned_locations.add(location)
return redirect(reverse('location_manager'))