I'm attempting to add a new foreign key to an existing model of mine, but I'm having issues getting it to save the values from the UI. When I get to the relevant page, the data populates correctly, but when I attempt to save a change of the foreign key I get a 1054 "Unknown column 'schedule_id' in 'field list'" error. Strangely, if I refresh the page, the changes were saved regardless of the error.
Relevant models:
class Group(BaseModel):
code = CharField()
name = CharField()
calendar = models.ForeignKey(Calendar)
schedule = models.ForeignKey(Schedule, null=True, blank=True, default=None) # this is the new FK
class Schedule(BaseModel):
code = CharField()
name = CharField()
description = CharField()
#various integer fields
I'm using South for my database migrations:
def forwards(self, orm):
db.add_column('group', 'schedule',
self.gf('django.db.models.fields.related.ForeignKey')\
(to=orm['app.Schedule'], blank=True, null=True)\
)
The view is pretty simple:
def api_group(jsonDict, request):
if request.method == "POST":
#other handlers
elif operation == "edit":
_update_group(request)
def _update_group(request):
group = Group.objects.get(id=request.POST.get('id'))
formData = request.POST.copy()
form = GroupForm(formData, instance=group)
if form.is_valid():
form.save()
class GroupForm(renderutils.BaseModelForm):
id = forms.CharField(widget=forms.HiddenInput())
class Meta(renderutils.BaseModelForm.Meta):
model = Group
When I look at the 'group' table, I can see the field named 'schedule_id' and correct values are getting assigned on save, I just don't understand why the error is being raised if nothing is going wrong? I've tried doing a backward and forward south migration multiple times to make sure it wasn't something wrong with it.
Related
I noticed that when using Django admin and whenever select/change multiple instances and click on save button (for example see the below image, it's not directly related to the code below), Django will clean/validate all instances and then save them one by one.
is this how things are working in Django or the process should be clean and then save the instance before repeat same process with the next instance? because when trying to set is_active value to be true for multiple instances, it passing the clean method condition without shown the error message that tells should only one instance be selected as true and that's correct cause no one of the instances have is_active as true in the database yet But if I click the save button again will show the error message.
models.py:
class SupplierAddress(models.Model):
"""Model to create supplier's address instances"""
class Meta:
"""Customize django default way to plural the class name"""
verbose_name = 'Supplier Address'
verbose_name_plural = 'Supplier Addresses'
constraints = [
models.UniqueConstraint(
fields=['supplier', 'address'],
name='supplier_address_unique_appversion'
)
]
# Define model fields.
supplier = models.ForeignKey(
'Supplier',
on_delete=models.CASCADE,
related_name='supplier_addresses_supplier'
)
address = models.ForeignKey(
'Address',
on_delete=models.CASCADE,
related_name='supplier_addresses_address'
)
is_active = models.BooleanField(default=False)
def clean(self):
"""Restrict the add/change to model fields"""
if self.is_active is True:
if SupplierAddress.objects.filter(
supplier=self.supplier,
is_active=True
).exclude(id=self.id).count() >= 1:
raise forms.ValidationError(
{
"is_active": "You can't set more than one active address"
}
)
So, I was able to reproduce your issue. What happens is that Django admin page uses a formset to save data in this kind of editable list. What you need is to override this formset (thanks to this answer) and add validation to it, something like:
from django.forms import BaseModelFormSet
class MyAdminFormSet(BaseModelFormSet):
def clean(self):
active_count = 0
form_set = self.cleaned_data
for form_data in form_set:
if form_data['is_active']:
active_count += 1
if active_count > 1:
raise forms.ValidationError('Cannot have more than one active object')
return form_set
class MyModelAdmin(admin.ModelAdmin):
list_display = (..., 'is_active')
list_editable = ('is_active',)
def get_changelist_formset(self, request, **kwargs):
kwargs['formset'] = MyAdminFormSet
return super().get_changelist_formset(request, **kwargs)
Note that you need to adapt MyAdminFormSet to your problem, I just did a shallow counting of active objects in mine.
I have a legacy DB on PostgreSQL with a simple products table with 2 columns (productid, productname) with 70 products. I am trying to start using Django as a frontend and I have the model as below and a model form. The save process is failing with the error below.
duplicate key value violates unique constraint "productidentifier"
DETAIL: Key (productid)=(4) already exists.
Could anyone help correct the error. I have confirmed on the Postgres table that the index get sequence is set right, expected value should be 72. See below the model, form and view.
#models.py
class Products(models.Model):
productid = models.AutoField(primary_key=True)
productname = models.TextField(verbose_name='Product Name')
class Meta:
managed = False
#Views.py
def new_product(request):
if request.method == 'POST':
form= forms.EditProductForm(request.POST)
if form.is_valid():
product = form.save()
return redirect('views.full_product_list')
else:
form = forms.EditProductForm()
return render(request, 'edit_product.html', {'form': form})
#forms.py
class EditProductForm(forms.ModelForm):
productname = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
class Meta:
model = models.Products
fields = ('productname')
There is propably some issue with your auto field sequence. You can use ./manage.py sqlsequencereset to fix that. It will generate SQL queries to fix sequence on your tables.
In my models.py
class Alert(models.Model):
user = models.CharField(max_length=30, blank=True)
a = models.IntegerField(blank=True)
def __unicode__(self):
return "'%s' at %s" % (self.user)
In My forms.py:
class AlertForm(forms.ModelForm):
class Meta:
model=Alert
fields = ('a','user')
widgets = {
'user': forms.HiddenInput()
}
AlertCountFormset = modelformset_factory(Alert,
form = AlertForm)
In my views.py:
def profile_setting(request, slug):
if request.method == 'GET':
form = AlertForm(request.POST)
if form.is_valid():
alert_form = form.save(commit=False)
alert_form.user = request.user.username
alert_form.save() # Here i am getting the error
return HttpResponseRedirect('/user/list')
extra_context = {
'form': AlertForm()
}
return direct_to_template(request,'users/profile_setting.html',
extra_context)
I am trying to fill the Django model Form but i am getting following error where i had put the comment:
events_alertcount.a may not be NULL
What is this? Even after putting thenull=True in the field of a it shows an same error. Is that something wrong with my forms.py or models.py?
This is enforced on database level, too. Set your "a" column in your db to allow the field to be NULL. This should fix it. HTH.
try this:
a = models.IntegerField(blank=True, null=True)
and you should call syncdb once again
When defining a model field, the blank option is validation related, meaning that if you set blank to true, validation will not fail if that field is not filled in.
blank is validation-related. If a field has blank=True, validation on Django’s admin site will allow entry of an empty value. If a field has blank=False, the field will be required.
However, if validation doesn't fail and you save the model, the field will be persisted to the database. Now, a field cannot be null in the database unless you set the null option to true.
null is purely database-related, whereas blank is validation-related.
Having said that, you can fix you error by adding the null option to Alert.a:
a = models.IntegerField(blank=True, null=True)
Now, if you've already ran the syncdb command, you need to drop your tables and then rerun syncdb in order for this change to be picked up. If this database is a production database and you cannot do this, look into django-south on how you can migrate your schemas and data.
I imported my (PHP) old site's database tables into Django. By default it created a bunch of primary key fields within the model (since most of them were called things like news_id instead of id).
I just renamed all the primary keys to id and removed the fields from the model. The problem then came specifically with my News model. New stuff that I add doesn't appear in the admin. When I remove the following line from my ModelAdmin, they show up:
list_display = ['headline_text', 'news_category', 'date_posted', 'is_sticky']
Specifically, it's the news_category field that causes problems. If I remove it from that list then I see my new objects. Now, when I edit those items directly (hacking the URL with the item ID) they have a valid category, likewise in the database. Here's the model definitions:
class NewsCategory(models.Model):
def __unicode__(self):
return self.cat_name
#news_category_id = models.IntegerField(primary_key=True, editable=False)
cat_name = models.CharField('Category name', max_length=75)
cat_link = models.SlugField('Category name URL slug', max_length=75, blank=True, help_text='Used in URLs, eg spb.com/news/this-is-the-url-slug/ - generated automatically by default')
class Meta:
db_table = u'news_categories'
ordering = ["cat_name"]
verbose_name_plural = "News categories"
class News(models.Model):
def __unicode__(self):
return self.headline_text
#news_id = models.IntegerField(primary_key=True, editable=False)
news_category = models.ForeignKey('NewsCategory')
writer = models.ForeignKey(Writer) # todo - automate
headline_text = models.CharField(max_length=75)
headline_link = models.SlugField('Headline URL slug', max_length=75, blank=True, help_text='Used in URLs, eg spb.com/news/this-is-the-url-slug/ - generated automatically by default')
body = models.TextField()
extra = models.TextField(blank=True)
date_posted = models.DateTimeField(auto_now_add=True)
is_sticky = models.BooleanField('Is this story featured on the homepage?', blank=True)
tags = TaggableManager(blank=True)
class Meta:
db_table = u'news'
verbose_name_plural = "News"
You can see where I've commented out the autogenerated primary key fields.
It seems like somehow Django thinks my new items don't have news_category_ids, but they definitely do. I tried editing an existing piece of news and changing the category and it worked as normal. If I run a search for one of the new items, it doesn't show up, but the bottom of the search says "1 News found", so something is going on.
Any tips gratefully received.
EDIT: here's my ModelAdmin too:
class NewsCategoryAdmin(admin.ModelAdmin):
prepopulated_fields = {"cat_link": ("cat_name",)}
list_display = ['cat_name', '_cat_count']
def _cat_count(self, obj):
return obj.news_set.count()
_cat_count.short_description = "Number of news stories"
class NewsImageInline(admin.TabularInline):
model = NewsImage
extra = 1
class NewsAdmin(admin.ModelAdmin):
prepopulated_fields = {"headline_link": ("headline_text",)}
list_display = ['headline_text', 'news_category', 'date_posted', 'is_sticky'] #breaking line
list_filter = ['news_category', 'date_posted', 'is_sticky']
search_fields = ['headline_text']
inlines = [NewsImageInline]
The answer you are looking for I think would lie in the SQL schema that you altered and not in the django models.
It could probably have something to do with null or blank values in the news_category_id, or news that belongs to a category that doesn't exist in the news_category. Things I'd check:
You have renamed the primary key on the News category from news_category_id to id. Does the foreign key on the News also map to news_category_id and not anything else?
Are all the values captured in the news.news_category also present in news_category.id
Also, as an aside, I don't see any reason why you need to rename the primary keys to id from something that they already are. Just marking them primary_key=True works just fine. Django provides you a convenient alias pk to access a model's integer primary key, irrespective of what the name of the field actually is.
I've been trying to reset my database and figure out why this is happening, but for some reason I'm getting this error:
IntegrityError: main_funding_rounds_investments.investment_id may not be NULL
I can't figure out how or why the automatic id field would be null? If anyone has ideas I would greatly appreciate it!
When i check the db with inspectdb, I get the following:
class MainFundingRoundsInvestments(models.Model):
id = models.IntegerField(primary_key=True)
funding_rounds_id = models.IntegerField()
investment_id = models.IntegerField()
class Meta:
db_table = u'main_funding_rounds_investments'
and
class MainInvestment(models.Model):
id = models.IntegerField(primary_key=True)
class Meta:
db_table = u'main_investment'
Here are my models:
#funding rounds
class funding_rounds(models.Model):
funded_day = models.IntegerField(null=True)
investments = models.ManyToManyField("Investment")
class Investment(models.Model):
company = models.ManyToManyField("Company", null=True, related_name ="Investments_company")
financial_org = models.ManyToManyField("Financial_org", null=True, related_name ="Investments_financial_org")
person = models.ManyToManyField("Person", null=True, related_name ="Investments_person")
This is where I create the objects:
def save_dict_to_db_R(model_class ,dicta):
testCo = model_class()
for key,value in dicta.items():
try:
field = model_class._meta.get_field(key)
if isinstance(field, models.ManyToManyField):
continue
else:
if model_class._meta.get_field(key):
print("i == something:" + key + " ")
setattr(testCo, key, value)
except FieldDoesNotExist:
continue
testCo.save(
for field in model_class._meta.many_to_many:
if field.name in dicta and hasattr(dicta[field.name], 'append'):
for obj in dicta[field.name]:
rel_instance = save_dict_to_db_R(field.rel.to, obj)
getattr(testCo, field.name).add(rel_instance)
Why have you set the primary key field manually? Unless you need to call the field something different from id, or give it different attributes, you should just leave it out of the model and let Django add it automatically.
Your immediate problem is that you've used IntegerField instead of AutoField, but if you removed your definition automatically that would be done for you.
Try using AutoField