I originally had the standard default id field for my Django model. However I want to switch to a random 10-15 alphanumerical string.
Here's the string if you're curious (it works):
def random_string(chars=string.ascii_letters + string.digits):
size = random.randrange(10, 15)
return str(''.join(random.choice(chars) for _ in range(size)))
And here's my model. I simply added in the field and did migrate & makemigrations:
class Post(models.Model):
id = models.CharField(max_length=18, primary_key=True, default=random_string())
user = models.ForeignKey(User, blank=True, null=True)
content = models.TextField(null=True, blank=True)
Expectedly it wan't a smooth changeover - when I make a Post, I get the following non_field_error:
Non Errors: <bound method BaseForm.non_field_errors of <PostForm bound=False, valid=False, fields=(content;id;user)>>
[20/Mar/2018 09:38:37] "POST /post/ HTTP/1.1" 200 11482
views
def post(request):
if request.user.is_authenticated():
form_post = PostForm(request.POST or None, request.FILES or None)
if form_post.is_valid():
instance = form_post.save(commit=False)
instance.user = request.user
instance.save()
return HttpResponseRedirect('/')
else:
form_post = PostForm()
print('Errors:', form_post.errors) #prints nothing
print('Non Errors:', form_post.non_field_errors)
context = {
'form_post': form_post,
}
return render(request, 'post/post.html', context)
else:
return HttpResponseRedirect("/accounts/signup/")
Can anyone tell me why I get that non_field_error? Here's the accompanying form to the model:
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = [
'content',
'id',
'user'
]
non_field_errors is a method, not a regular attribute as errors is:
print('Non Errors:', form_post.non_field_errors())
Related
I have a form where I want request.user to populate as little as possible and rely on the views to populate other fields automatically.
As a result, some of these fields are not rendered on the form.
The code in my view seems to work fine for the FK relationship, but some reason the m2m is failing.
It's probably the first time I am trying to save a form with m2m and I am probably missing something.
At the moment the error I get with the current code is 'VoucherForm' object has no attribute 'user'.
If I remove voucherform.user.add(userprofile)from the views the form will save, but will not add the user.
model
class UserProfile(models.Model):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
class Voucher(models.Model):
user = models.ManyToManyField(User, blank=True)
venue = models.ForeignKey(Venue, blank=True, null=True, related_name="vouchervenues", on_delete=models.CASCADE)
title = models.TextField('voucher title', blank=True)
terms = models.TextField('terms & conditions', blank=True)
form
class VoucherForm(ModelForm):
class Meta:
model = Voucher
fields = ('title','terms')
labels ={
'title': '',
'terms': '',
}
widgets = {
'title': forms.TextInput(attrs={'class':'form-control', 'placeholder':'Enter title'}),
'terms': forms.TextInput(attrs={'class':'form-control', 'placeholder':'Enter terms'}),
}
views
def add_voucher(request, userprofile_id):
url = request.META.get('HTTP_REFERER')
venue = UserProfile.objects.filter(user=request.user).values('venue')
userprofile = UserProfile.objects.get(id=userprofile_id)
submitted = False
if request.method =="POST":
voucherform = VoucherForm(request.POST)
if voucherform.is_valid():
data = voucherform.save(commit=False)
data.user_id = userprofile.id
data.venue_id = venue
data.save()
voucherform.save_m2m()
voucherform.user.add(userprofile)
return HttpResponseRedirect(url)
else:
voucherform = VoucherForm
if 'submitted' in request.GET:
submitted=True
return redirect('venue-loyalty-card',{'submitted':submitted,'userprofile':userprofile})
Basically, the problem is that you haven't mentioned user field in VoucherForm at fields so it says 'VoucherForm' object has no attribute 'user', you can do the following:
from django.shortcuts import get_object_or_404
def add_voucher(request, userprofile_id):
url = request.META.get('HTTP_REFERER')
venue = UserProfile.objects.filter(user=request.user).values('venue')
userprofile = UserProfile.objects.get(id=userprofile_id)
submitted = False
if request.method =="POST":
voucherform = VoucherForm(request.POST)
if voucherform.is_valid():
data = voucherform.save(commit=False)
data.user_id = userprofile.id
data.venue_id = venue
data.save()
voucherform.save_m2m()
current_voucher_instance= get_object_or_404(Voucher,id=data.id)
current_voucher_instance.user.add(userprofile.id)
return HttpResponseRedirect(url)
else:
voucherform = VoucherForm
if 'submitted' in request.GET:
submitted=True
return redirect('venue-loyalty-card',{'submitted':submitted,'userprofile':userprofile})
Note: It is better to use get_object_or_404() than get() as it calls get() on a given model manager, but it raises Http404 instead of the model's DoesNotExist exception.
I have a ModelForm called ListingForm. It takes data from a user but I have stopped some of the model attributes from appearing in this form as I want to feed data to those myself. I have put print statements in my createlisting function in views.py to inspect if the data is actually being saved correctltly, it turns out the data is being saved. Here is the createlisting function:
def create_listing(request):
if request.method == 'POST':
import datetime
listing_form = ListingForm(request.POST, request.FILES)
if listing_form.is_valid():
bid = listing_form.cleaned_data['starting_bid']
print(bid)
listing_form.save(commit=False)
listing_form.user = request.user
print(listing_form.user)
listing_form.date_made = datetime.datetime.today()
listing_form.is_active = True
listing_form.category = Category.objects.get(name=listing_form.cleaned_data['listing_category'])
print(listing_form.category)
#The form is being saved correctly here, and the print statements give the correct results in my terminal
listing_form.save()
Bid.objects.create(user= request.user, value=bid, listing=listing_form.instance)
all_listings = Listing.objects.all()
return render(request, 'auctions/index.html', {
'all_listings': all_listings })
else:
listing_form = ListingForm()
return render(request, 'auctions/createlisting.html',{
'listing_form':listing_form
})
However, when I try to access the data from the model Listing from which the ListingForm is inheriting, the print statements I have put for debugging return the default values for certain fields (category and user) instead of the values I have saved in the ListingForm.
Here is the code that allows me to view the data for the model instance I have created. Mind you, all the other fields have saved correctly except for the fields category and user:
def view_listing(request, listing_id):
listing = Listing.objects.get(pk=listing_id)
#the print results return the default values for the fields category and user instead of the values I saved in my ModelForm
print(listing.category)
print(listing.user)
if request.user == listing.user:
return render(request, 'auctions/view_listing.html', {
'listing': listing,
'flag':True,
'count': listing.bids.all().count()
})
else:
return render(request, 'auctions/view_listing.html',{
'listing':listing,
'count': listing.bids.all().count()
})
What could be the problem with my code?
Also, let me provide the code for some of my models and a form as the error might be embedded in those:
Listing Model:
class Listing(models.Model):
NAME_CHOICES = [
('Fashion', 'Fashion'),
('Toys','Toys'),
('Electronics','Electronics'),
('Home', 'Home'),
('Other', 'Other')
]
title = models.CharField(max_length= 64)
date_made = models.DateTimeField(auto_now_add=True)
description = models.TextField()
user = models.ForeignKey(User, to_field='username', on_delete=models.CASCADE, related_name='user_listings', null=True)
starting_bid = models.DecimalField(decimal_places=2, max_digits=264, default=10.00)
upload_image = models.ImageField(blank=True, upload_to='media/media')
category = models.ForeignKey(Category, on_delete=models.CASCADE, to_field='name', related_name='category_listings', default=NAME_CHOICES[4][0], db_constraint=False)
listing_category = models.CharField(max_length=12, choices=NAME_CHOICES, null=True, default=NAME_CHOICES[4][0])
is_active = models.BooleanField(default=True)
watchlist = models.ForeignKey('Watchlist', on_delete=models.DO_NOTHING, related_name='listings', null=True)
Category Model:
class Category(models.Model):
NAME_CHOICES = [
('Fashion', 'Fashion'),
('Toys','Toys'),
('Electronics','Electronics'),
('Home', 'Home'),
('Other', 'Other')
]
name = models.CharField(max_length=12, choices= NAME_CHOICES, unique=True)
User Model:
class User(AbstractUser):
def __str__(self):
return f'{self.username} '
ListingForm`` (ModelForm```):
class ListingForm(ModelForm):
class Meta:
model = Listing
exclude = [
'date_made',
'user',
'category',
'is_active',
'watchlist'
]
Any form of help would be greatly appreciated.
When you call listing_form.save(commit=False) it returns an unsaved model instance with the submitted values. If you assign that to a variable, you can use it to set the other field values and save:
def create_listing(request):
if request.method == 'POST':
import datetime
listing_form = ListingForm(request.POST, request.FILES)
if listing_form.is_valid():
bid = listing_form.cleaned_data['starting_bid']
listing = listing_form.save(commit=False)
listing.user = request.user
listing.date_made = datetime.datetime.today()
listing.is_active = True
listing.category = Category.objects.get(name=listing_form.cleaned_data['listing_category'])
listing.save()
Bid.objects.create(user=request.user, value=bid, listing=listing)
# You should probably use HttpResponseRedirect to an `all_listings` page, rather than displaying them here
all_listings = Listing.objects.all()
return render(request, 'auctions/index.html', {
'all_listings': all_listings })
Here's a link to the ModelForm.save() docs.
I have a problem, when i try upload files I am trying show the user uploaded the file but not working.
My model is this:
class Document(models.Model):
id = models.AutoField(primary_key=True)
programa=models.CharField(max_length = 100)
materia=models.CharField(max_length = 50)
profesor=models.CharField(max_length = 50)
usuario=models.ForeignKey(Usuario)
add=models.DateTimeField ( auto_now = True )
archivo= models.FileField(upload_to="archivos/",storage=OverwriteStorage(),null = False)
def __unicode__(self):
return self.programa
class Meta:
db_table = u'utp_document'
My view is:
#login_required(login_url='/')
def upload(request):
if request.method=='POST':
form=DocumentForm(request.POST,request.FILES,)
if form.is_valid():
instances = form.save(commit=False)
for instance in instances:
instance.usuario = request.user
instance.save()
return HttpResponseRedirect('/menu/')
else:
form=DocumentForm()
return render_to_response('formulario_modal.html', {'form': form}, context_instance=RequestContext(request))
I followed this post Django - Auto populate created_by field outside django admin using form.
and this my form.py:
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
exclude = ('usuario',)
The problem is what i try new upload file get this error:
'Document' object is not iterable
What am I doing wrong?
form.save() returns a single modal instance:
instance = form.save(commit=False)
instance.usuario.user = request.user
instance.save()
FYI, in the post you've linked a formset is used which is the reason for a loop.
I'm trying to make a view where the user can edit DB records through a form in a template. I've searched a lot of web pages (and Django docs as well) where they teach how to make these views, but they always use the "id" that Django generates for each Model. In this particular Model, I have to use an AutoField to override the "id". Is there a way to use this AutoField as an "id" of the record with Django?
Here's my complete model:
class T031003 (models.Model):
C003IDCD = AutoField(primary_key=True)
C003INST = models.IntegerField(unique=True) #usar AutoSlug
C003TPCD = models.CharField(max_length=1)
C003CHCD = models.CharField(max_length=14)
C003MTR = models.CharField(max_length=30, blank=True, null=True)
C003CTCD = models.CharField(max_length=3)
C003RZSC = models.CharField(max_length=60, blank=True, null=True)
C003EML = models.EmailField(max_length = 254, blank=True, null=True)
C003LOGA = models.CharField(max_length=20)
C003LOGB = models.DateTimeField()
C003LOGD = models.CharField(max_length=15, blank=True, null=True)
C003LOGF = models.CharField(max_length=20, blank=True, null=True)
def __unicode__(self):
return '%s' % self.C003MTR
class T031003Form(ModelForm):
class Meta:
model = T031003
ordering = ["-C003MTR"]
exclude = ('C003LOGA','C003LOGB','C003LOGD','C003LOGE','C003LOGF')
And here's the view I tried to do, but it gives me the error "No T031003 matches the given query." and it's right, since there is no "id" in the table:
def t031003form_edit(request, id=None):
pin = get_object_or_404(T031003, pk=id)
form = T031003Form(request.POST or None, instance=pin)
if request.method == 'POST':
if form.is_valid():
form = form.save(False)
form.C003LOGA = request.user
form.C003LOGB = datetime.date.today()
form.C003LOGD = request.META['REMOTE_ADDR']
form.C003LOGF = request.META['USERDOMAIN']
form.save()
form = T031003Form()
else:
return HttpResponseRedirect('/erro/')
return render_to_response('T031003Form_edit.html', {'form': form,}, context_instance=RequestContext(request))
Any help would be very appreciated!
If a model has an AutoField — an auto-incrementing primary key — then that auto-incremented value will be calculated and saved as an attribute on your object the first time you call save():
>>> b2 = Blog(name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b2.id # Returns None, because b doesn't have an ID yet.
>>> b2.save()
>>> b2.id # Returns the ID of your new object.
There's no way to tell what the value of an ID will be before you call save(), because that value is calculated by your database, not by Django.
ref : https://docs.djangoproject.com/en/dev/ref/models/instances/?from=olddocs
Well, thanks to the help from a close friend, I could do the trick using formsets. Here's the view:
def t031002form_edit(request, id_auto):
j = get_object_or_404(T031002, pk=id_auto)
T031003FormSet = modelformset_factory(T031002, can_delete=True, max_num=1)
if request.method == 'POST':
form = T031002FormSet(request.POST or None, request.FILES or None, queryset=T031002.objects.filter(pk=id_auto))
if form.is_valid():
instance = form.save(commit=False)
form.C003LOGA = request.user
form.C003LOGB = datetime.date.today()
form.C003LOGD = request.META['REMOTE_ADDR']
form.C003LOGF = request.META['USERDOMAIN']
for reform in instance:
reform.save()
else:
return HttpResponseRedirect('/erro/')
else:
form = T031002FormSet(queryset=T031002.objects.filter(pk=id_auto))
return render_to_response(('T031002Form_edit.html'), {'form': form,}, context_instance=RequestContext(request))
So, with formsets, you can work nicely and with no worries. Hope it helps others with this same questioning.
I am using django-selectable and having trouble saving the "id" ('autocomplete_1') which represents the id for the category --- from the template.
models.py
class Category(models.Model):
cat_no = models.IntegerField(null=True, blank=True)
cat_txt = models.CharField(max_length=45)
def __unicode__(self):
return self.cat_txt
class Cattest(models.Model):
category = models.ForeignKey(Category)
info = models.CharField(max_length=35, blank=True)
lookups.py
class CategoryLookup(ModelLookup):
model = Category
search_fields = ('cat_txt__icontains', )
forms.py
class CategoryForm(forms.Form):
autocomplete = forms.CharField(
label='Type the name of a category (AutoCompleteWidget)',
widget=selectable.AutoCompleteWidget(CategoryLookup),
required=False,
)
autocompleteselect = selectable.AutoCompleteSelectField(
lookup_class=CategoryLookup,
label='Select a category (AutoCompleteField)',
required=False,
)
class CattestForm(forms.Form):
#model = cattest
#fields = ('category', 'info')
autocomplete = forms.CharField(
label='Type the name of a category (AutoCompleteSelectWidget)',
widget=selectable.AutoCompleteSelectWidget(CategoryLookup),
required=False,
)
info = forms.CharField(max_length=35, label="Information")
views.py
def cattest(request):
if request.method == 'POST':
form = CattestForm(request.POST)
if form.is_valid():
cattest = Cattest.objects.create(
category=form.cleaned_data['autocomplete_1'],
info=form.cleaned_data['info'],
)
# Always redirect after a POST
return http.HttpResponseRedirect('/bsmain/login_customer')
else:
if request.GET:
form = CattestForm(initial=request.GET)
else:
form = CattestForm()
return render_to_response('bsmain/form.html', {'form': form}, context_instance=RequestContext(request))
Traceback:
KeyError at /bsmain/cattest/
'autocomplete_1'
Request Method: POST
Request URL: http://127.0.0.1:8000/bsmain/cattest/
Django Version: 1.3.1
Exception Type: KeyError
Exception Value:
'autocomplete_1'
Exception Location: /home/bill/workspace/boatsite/../boatsite/bsmain/views.py in cattest, line 64
Python Executable: /usr/bin/python
Python Version: 2.6.5
Request information
GET No GET data
POST Variable Value
info u'44# Bruce'
csrfmiddlewaretoken u'9ffe49bd68be04087521e71e86a5bec0'
autocomplete_1 u'10'
autocomplete_0 u'Anchors'
The form cleaned_data dictionary is populated based on the field names. You should access the data from form.cleaned_data['autocomplete'].
Edit:
This would probably be easier by using a ModelForm:
class CattestForm(forms.ModelForm):
class Meta:
model = Cattest
fields = ('category', 'info', )
widgets = {
'category': selectable.AutoCompleteSelectWidget(CategoryLookup)
}
def cattest(request):
if request.method == 'POST':
form = CattestForm(request.POST)
if form.is_valid():
cattest = form.save()
# Always redirect after a POST
return http.HttpResponseRedirect('/bsmain/login_customer')
else:
initial = request.GET or None
form = CattestForm(initial=initial)
return render_to_response('bsmain/form.html', {'form': form}, context_instance=RequestContext(request))