Not required many-to-many relationship giving ValueError - django

I'm having an issue where I have a non required self many-to-many relationship that when saving a new object to an empty psql db gives me:
Edit: This is when I'm admin saving, there is no view that saves the model.
ValueError: "Video: Teste" needs to have a value for field "from_video" before this many-to-many relationship can be used.
This is my model:
class Video(models.Model):
title = models.CharField(max_length=200, unique=True)
subtitle = models.CharField(max_length=400)
thumbnail = models.ImageField(upload_to='videos/thumbnails')
related_videos = models.ManyToManyField('self', symmetrical=False, blank=True)
This is my save function:
def save(self, *args, **kwargs):
if self.id is None:
# Elasticsearch document creation if word does not exist
video = VideoDocType(title=self.title, subtitle=self.subtitle, thumbnail=str(self.thumbnail))
video.save()
else:
old_value = Video.objects.get(id=self.id)
thumbnail_url = str(self.thumbnail)
video = self._get_video(self)
if video is None:
video = VideoDocType(title=self.title, subtitle=self.subtitle, thumbnail=str(self.thumbnail))
video.save()
else:
if old_value.thumbnail != self.thumbnail:
thumbnail_url = ("videos/thumbnails/" + thumbnail_url)
video.update(title=self.title, subtitle=self.subtitle, thumbnail=str(self.thumbnail))
super(Video, self).save(*args, **kwargs)
My question is, why a non required field gives me the ValueError when there is nothing to be added on the many-to-many field? And how could I fix this?

Related

Update a model field in save method based on other field values selected including m2m field

I am trying to update a model field by overriding save method:
class DataTable(models.Model):
id = models.AutoField(db_column='Id', primary_key=True)
country= models.ForeignKey(Countries,on_delete=models.DO_NOTHING,db_column='CountryId')
university=models.ManyToManyField(Universities,db_column='UniversityId',verbose_name='University',related_name='universities')
intake = models.CharField(db_column='Intake',blank=True, null=True, max_length=20, verbose_name='Intake')
year = models.CharField(max_length=5,blank=True,null=True)
application_status = models.ForeignKey(Applicationstages,on_delete=models.DO_NOTHING, db_column='ApplicationStageId',verbose_name='Application Status')
requested_count = models.PositiveIntegerField(db_column='RequestedCount',blank=True,null=True)
class Meta:
db_table = 'DataTable'
def __str__(self):
return str(self.application_status)
def __unicode__(self):
return str(self.application_status)
def save(self,*args, **kwargs):
super(DataTable,self).save(*args, **kwargs)
country_id= self.country
intake= self.intake
year= self.year
universities= self.university.all()
courses = get_ack_courses(country_id,universities)
all_data = get_all_courses(intake,year,courses)
ack_data = get_acknowledgements_data(all_data)
self.requested_count =len(ack_data)
super(DataTable,self).save(*args, **kwargs)
I am trying to update the requested_count field using the other field values, but in save method when I try to get the m2m field data ; it is returning empty. I tried with post_save signal also and there also m2m field data is empty.
I want the count field based on otherfields from this model. How to save when we have m2m fields?

Django: How to check a Form with a m2m relation object already exists or is “unique_together”?

I am testing forms and nesting models in django. In my Project a Person can enter departure, arrival (city names) and choose a weekly day (Mon-Fri). Maybe he drives every “Tuesday” from Amsterdam to Paris. I wanted this constellation to be unique – just for fun. So If another user enters the same route the relation should be linked to the same Car.object.
Models.py
class Person(models.Model):
name = models.CharField(max_length=255, blank=False, unique=True)
route = models.ManyToManyField('Car')
def __str__(self):
return self.name
class Car(models.Model):
name = models.CharField(max_length=255, blank=False, unique=True)
weekdays = models.ForeignKey('Week', null=True, blank=False, on_delete=models.SET_NULL)
departure = models.CharField(max_length=255, blank=False)
arrival = models.CharField(max_length=255, blank=False)
class Meta:
unique_together = ['weekdays', 'departure', 'arrival'] # --- Unique combination
def __str__(self):
return self.name
class Week(models.Model):
day = models.CharField(max_length=255, blank=False, unique=True)
def __str__(self):
return self.day
views.py
class RouteCreateView(CreateView):
model = Person
template_name ="testa/create_route.html"
form_class = RouteForm
success_url = reverse_lazy('testa:testa_home')
def form_valid(self, form):
return super().form_valid(form)
forms.py
class RouteForm(forms.ModelForm):
# --- apply ChoiceField
day = forms.ModelChoiceField(queryset=None)
car_name = forms.CharField()
departure = forms.CharField()
arrival = forms.CharField()
class Meta:
model = Person
fields = [
'name'
]
def __init__(self, *args, **kwargs):
super(RouteForm, self).__init__(*args, **kwargs)
self.fields['day'].queryset = Week.objects.all()
def save(self, commit=True):
personData = super().save(commit)
data = self.cleaned_data
carData = Car(name=data['car_name'], weekdays=data['day'], departure=data['departure'], arrival=data['arrival'])
if commit:
carData.save()
personData.route.add(carData) # --- save m2m relation
return personData
If i enter two times for example „“Tuesday” from Amsterdam to Paris “ then an Error Message appears obviously, this error message (it´s german), telling me I have a double entry / Key.
Question
So my save()Method does not work because I need some kind of logic, so that Django takes the existing car.object or creates a new - if it is not a double entry. But I do not know where to start? The easiest way would be to get some kind of response from my model meta option Car.unique_together so "if it´s an “double-key error” then take the existing object". Is there a way to fetch the response? And what kind of Values it would be, only errors, could not find any hint in the doc? Or should I try some logic with exists()
That was my kind of idea / approach of a new save() 😊
def save(self, commit=True):
personData = super().save(commit)
data = self.cleaned_data
carData = Car(name=data['car_name'], weekdays=data['day'], departure=data['departure'], arrival=data['arrival'])
if commit:
# Check if database sends unique_together response
# if yes
if Car.Meta.unique_together is True:
getAlternative = Car.object.get(Meta.unique_together) # --- get the object which already exist
personData.route.add(getAlternative) # --- save m2m relation
# if not
else:
carData.save() # --- save object
personData.route.add(carData) # --- save m2m relation
return personData
obviously i get a error message: type object 'Car' has no attribute
'Meta'
Theres get_or_create for such use case: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#get-or-create
...
car, created = Car.objects.get_or_create(
weekdays=data['day'],
departure=data['departure'],
arrival=data['arrival'],
defaults = dict(name=data['car_name']),
)
personData.route.add(car)
...
Obviously given name gets ignored if another car with same weekdas, departure, arrival has been found.
I suggest to put the code for creating the car and adding the route in a transaction.atomic() https://docs.djangoproject.com/en/2.2/topics/db/transactions/#django.db.transaction.atomic

How to create a filter queryset on an array so that all search terms are required?

I have a Photo model with tags. I want to be able to create a query such that only photos having all search tag terms will be returned. The current behavior (using the view code below) returns photos having any of the tags being searched on.
#list_route(methods=['post'])
def tags(self, request):
"""
search for entities by tags they contain
:param request:
:return: Response
"""
tags = json.loads(request.data['tags'])
photos = Photo.objects.filter(tags__name__in=tags, owner=self.request.user).distinct()
page = self.paginate_queryset(photos)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(photos, many=True)
return Response(
data=serializer.data
)
Here is the model class:
class Photo(TimestampModerated):
owner = models.ForeignKey('auth.User', related_name='photos', on_delete=models.CASCADE)
uuid = models.UUIDField(default=uuid4, editable=False)
slug = models.SlugField(max_length=80, editable=False)
title = models.CharField(max_length=80, blank=False, null=False)
description = models.TextField(verbose_name='description of entity', blank=True, null=True)
photo = models.ImageField(upload_to=user_directory_path, height_field="height", width_field="width", blank=True)
height = models.IntegerField(blank=True)
width = models.IntegerField(blank=True)
tags = TaggableManager(blank=True)
hash = models.CharField(max_length=64, unique=True, null=True)
class Meta:
verbose_name_plural = "photos"
def __str__(self):
return self.title
def delete(self, using=None, keep_parents=False):
default_storage.delete("{}".format(self.photo))
super().delete()
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Photo, self).save(*args, **kwargs)
I'm using django-rest-framework to create the views and django-taggit for the tagging.
One way is to loop over the list of terms adding filter terms:
photos = Photos.objects.filter(owner=self.request.user)
for tag in tags:
photos = photos.filter(tags__name=tag)
photos = photos.distinct()
Combining Q objects is not suitable here, because as the docs explain Django requires all constraints in a single filter clause to be met by the same related object - ANDing together multiple Q objects would require that one tag have a name equal to each term in the list.

How can I find out why a Django database .save() failed?

My Django database .save() is throwing an exception. Is there a way to find out why?
My main code is:
for i in data['movies']:
try:
id = i['regions'][0]['products'][0]['product_id']
title = i['regions'][0]['products'][0]['product_title']
m = Movie.objects.get(preview_id=id)
except Movie.DoesNotExist:
try:
movie = Movie()
movie.preview_id = id
movie.title = title
movie.details = i['regions'][0]['products'][0]['description']
movie.date = i['regions'][0]['products'][0]['upd_date']
movie.trailer = i['regions'][0]['products'][0]['clips'][0]['files'][0]['url']
movie.likes = 0
movie.dislikes = 0
movie.save() # THIS IS THROWING AN ERROR
print id + ": " + title
for genre in i['regions'][0]['categories']:
try:
g = Genre.objects.get(title__exact=genre['name'])
movie.genres.add(g)
except Genre.DoesNotExist:
g = Genre(title=genre['name'])
g.save()
movie.genres.add(g)
for pic in i['regions'][0]['pictures']:
if pic['type_name'] == "poster_large":
movie.picture = pic['url']
movie.save()
except:
print 'error'
print 'Success'
My movies model looks like this:
class Movie(models.Model):
# Movie Model store all the detail of a movie
# Each movie is created by a User
user = models.ForeignKey(User)
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, blank=True, null=True)
details = models.TextField()
picture = models.ImageField(upload_to=filename, blank=True, null=True)
trailer = models.FileField(upload_to=videoname, blank=True, null=True)
# genres = ManyToMany with Genre Model
genres = models.ManyToManyField(Genre, related_name='genres')
likes = models.BigIntegerField(blank=True, null=True)
dislikes = models.BigIntegerField(blank=True, null=True)
trigahs = models.BigIntegerField(blank=True, null=True)
# Director with People Model
director = models.ForeignKey(People, blank=True, null=True)
# Casts = ManyToMany with People Model
casts = models.ManyToManyField(People, related_name='casts', blank=True, null=True)
date = models.DateField(blank=True, null=True)
amazon_id = models.CharField(max_length=200, blank=True, null=True)
preview_id = models.BigIntegerField(blank=True, null=True)
created_on = models.DateTimeField(auto_now_add=True)
def add_likes(self):
self.likes = self.likes + 1
self.save()
def add_dislikes(self):
self.dislikes = self.dislikes + 1
self.save()
def save(self):
super(Movie, self).save()
if not self.slug:
self.slug = '%s' % (
slugify(self.title)
)
super(Movie, self).save()
And the super function is defined as follows:
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
"""
Saves the current instance. Override this in a subclass if you want to
control the saving process.
The 'force_insert' and 'force_update' parameters can be used to insist
that the "save" must be an SQL insert or update (or equivalent for
non-SQL backends), respectively. Normally, they should not be set.
"""
# Ensure that a model instance without a PK hasn't been assigned to
# a ForeignKey or OneToOneField on this model. If the field is
# nullable, allowing the save() would result in silent data loss.
for field in self._meta.concrete_fields:
if field.is_relation:
# If the related field isn't cached, then an instance hasn't
# been assigned and there's no need to worry about this check.
try:
getattr(self, field.get_cache_name())
except AttributeError:
continue
obj = getattr(self, field.name, None)
# A pk may have been assigned manually to a model instance not
# saved to the database (or auto-generated in a case like
# UUIDField), but we allow the save to proceed and rely on the
# database to raise an IntegrityError if applicable. If
# constraints aren't supported by the database, there's the
# unavoidable risk of data corruption.
if obj and obj.pk is None:
raise ValueError(
"save() prohibited to prevent data loss due to "
"unsaved related object '%s'." % field.name
)
using = using or router.db_for_write(self.__class__, instance=self)
if force_insert and (force_update or update_fields):
raise ValueError("Cannot force both insert and updating in model saving.")
if update_fields is not None:
# If update_fields is empty, skip the save. We do also check for
# no-op saves later on for inheritance cases. This bailout is
# still needed for skipping signal sending.
if len(update_fields) == 0:
return
update_fields = frozenset(update_fields)
field_names = set()
for field in self._meta.fields:
if not field.primary_key:
field_names.add(field.name)
if field.name != field.attname:
field_names.add(field.attname)
non_model_fields = update_fields.difference(field_names)
if non_model_fields:
raise ValueError("The following fields do not exist in this "
"model or are m2m fields: %s"
% ', '.join(non_model_fields))
# If saving to the same database, and this model is deferred, then
# automatically do a "update_fields" save on the loaded fields.
elif not force_insert and self._deferred and using == self._state.db:
field_names = set()
for field in self._meta.concrete_fields:
if not field.primary_key and not hasattr(field, 'through'):
field_names.add(field.attname)
deferred_fields = [
f.attname for f in self._meta.fields
if (f.attname not in self.__dict__ and
isinstance(self.__class__.__dict__[f.attname], DeferredAttribute))
]
loaded_fields = field_names.difference(deferred_fields)
if loaded_fields:
update_fields = frozenset(loaded_fields)
self.save_base(using=using, force_insert=force_insert,
force_update=force_update, update_fields=update_fields)
save.alters_data = True
This is all code I am taking over so much of it is a mystery to me. Apologies if this question is not properly framed. But could really do with a pointer as to how to track down the reason for failure.
try:
line 1
line 2
except Exception as e:
print e
This will reveal the error. This is only for debugging. You should properly handle the exceptions.

IntegrityError in django mptt - column not unique

I'm having trouble saving a form in a Django app. I want to create an model called 'dataset' from another model called 'image', both of which are mptt models.
models
class Image(MPTTModel):
name = models.CharField(max_length=50, unique=True)
parent = TreeForeignKey('self', null=True, blank=True, related_name='children')
def __unicode__(self):
return self.name
class Dataset(MPTTModel):
name = models.CharField(max_length=50, unique=True)
image = TreeForeignKey(Image, null=True, blank=True, unique=True, related_name='image')
def __unicode__(self):
return self.name
class MPTTMeta:
parent_attr = 'image'
When I try to save a Dataset, I get an integrity error:
IntegrityError at /associate/
column image_id is not unique
Request Method: GET
Request URL: http://127.0.0.1:8000/associate/
Django Version: 1.6.2
Exception Type: IntegrityError
Exception Value:
column image_id is not unique
Exception Location: C:\Python27\lib\site-packages\django\db\backends\sqlite3\base.py in execute, line 450
views.py
def index(request):
images = Image.objects.all()
datasets = []
for i in images:
if i.rank() >= 3:
dataset = Dataset(image=i, name=i.name)
dataset.save()
datasets.append(dataset)
return render(request, 'associate/index.html', {'datasets':datasets})
def learn(request):
if request.method == 'POST':
try:
dataset = request.POST.get('dataset', False)
model = Dataset.objects.get(name=dataset)
if model:
print model.name
else:
print "no model"
except Dataset.DoesNotExist:
return render(request, 'associate/index.html')
else:
return render(request, 'associate/learn.html', {'dataset':model})
You have unique=True in your Dataset model for image field. This means you cannot assign one image to different Dataset instances. But you are doing in it in your index. In your index you trying to create a new dataset for every image every time. But if the Dataset with this image already created - you will get this 'column image_id is not unique' error. Please review your application logic and either remove unique=True or rewrite the behavior.