How do I solve django.db.utils.IntegrityError: UNIQUE constraint failed?
error code is
django.db.utils.IntegrityError: UNIQUE constraint failed: Movies_comment.user_id, Movies_comment.tv_or_movie_id.
This error occurs Comment(comment=form.cleaned_data["comment"],user=request.user,stars=form.cleaned_data["stars"],tv_or_movie=tv_or_movie_object).save()
views.py
def view_tv_and_movie_detail(request, type_movie_or_tv, id):
tv_or_movie_object, _ = TVAndMovie.objects.get_or_create(tmdb_id=id, judge_tv_or_movie=type_movie_or_tv)
detail_tv_or_movie = TvAndMovieDetailhelp(request, tv_or_movie_object, 3)
mycomment_obj = detail_tv_or_movie.get_user_post_comment_for_tv_or_movie()
if request.method == "POST":
if request.POST.get("action") == "delete":
mycomment_obj.delete()
return redirect("view_tv_and_movie_detail", type=type_movie_or_tv, id=id)
else:
form = CommentCreateForm(request.POST, instance=mycomment_obj)
if form.is_valid() and request.POST.get("action") == "update":
form.save()
return redirect("view_tv_and_movie_detail", type=type_movie_or_tv, id=id)
elif form.is_valid() and request.POST.get("action") == "create":
Comment(
comment=form.cleaned_data["comment"],
user=request.user,
stars=form.cleaned_data["stars"],
tv_or_movie=tv_or_movie_object,
).save()
return redirect("view_tv_and_movie_detail", type=type_movie_or_tv, id=id)
else:
form = CommentCreateForm(instance=mycomment_obj)
data = detail_tv_or_movie.get_object_tv_or_movie_data()
recommendations = detail_tv_or_movie.get_recommendations_tmdb_data()
pages = detail_tv_or_movie.get_page_comment()
average = tv_or_movie_object.average_stars()
context = {
"data": data,
"recommendations": recommendations,
"type": "movie",
"mycomment": mycomment_obj,
"average": average,
"form": form,
"pages": pages
}
return render(request, "Movie/movie_detail.html", context)
models.py
class TVAndMovie(models.Model):
tmdb_id = models.CharField(
validators=[alphanumeric], max_length=9999
)
judge_tv_or_movie = models.CharField(
blank=False, null=False, default="movie", max_length=20
)
stars = models.FloatField(
blank=False,
null=False,
default=0,
validators=[MinValueValidator(0.0), MaxValueValidator(10.0)],
)
def get_judge_tv_or_movie(self) -> str:
return self.judge_tv_or_movie
def get_comments(self) -> object:
return Comment.objects.prefetch_related("tv_or_movie").filter(
tv_or_movie_id=self.id
)
def average_stars(self) -> float:
comments = self.get_comments()
n_comments = comments.count()
if n_comments:
self.stars = round(
sum([comment.stars for comment in comments]) / n_comments, 3
)
else:
self.stars = 0
self.save()
return self.stars
class Comment(models.Model):
comment = models.TextField(max_length=1000)
stars = models.FloatField(
blank=False,
null=False,
default=0,
validators=[MinValueValidator(0.0), MaxValueValidator(10.0)],
)
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
tv_or_movie = models.ForeignKey(TVAndMovie, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ("user", "tv_or_movie")
indexes = [models.Index(fields=["user", "tv_or_movie"])]
def __str__(self) -> str:
return self.comment[:20]
helper_views.py
class TvAndMovieDetailhelp:
def __init__(self, request, obj_tv_or_movie: object, page_num: int) -> None:
self.request = request
self.obj_tv_or_movie: object = obj_tv_or_movie
self.page_num: int = page_num
def get_object_tv_or_movie_data(self) -> dict:
tv_or_movie_url: str = (
"https://api.themoviedb.org/3/" +
self.obj_tv_or_movie.judge_tv_or_movie +
"/" +
str(self.obj_tv_or_movie.tmdb_id) +
"?api_key=" +
TMDB_API_KEY +
"&language=en-US"
)
return (requests.get(tv_or_movie_url)).json()
def get_recommendations_tmdb_data(self) -> dict:
recommendations_url: str = (
"https://api.themoviedb.org/3/" +
self.obj_tv_or_movie.judge_tv_or_movie +
"/" +
str(self.obj_tv_or_movie.tmdb_id) +
"/recommendations?api_key=" +
TMDB_API_KEY +
"&language=en-US"
)
return (requests.get(recommendations_url)).json()
def get_comment_for_tv_or_movie(self) -> object:
if self.request.user.id is not None:
return (
Comment.objects.filter(tv_or_movie=self.obj_tv_or_movie.id)
.exclude(user=self.request.user)
.order_by("-updated_at")
)
else:
return Comment.objects.filter(tv_or_movie=self.obj_tv_or_movie.id).order_by(
"-updated_at"
)
def get_user_post_comment_for_tv_or_movie(self) -> object:
if self.request.user.id is not None:
try:
return Comment.objects.get(
user=self.request.user, tv_or_movie=self.obj_tv_or_movie
)
except Comment.DoesNotExist:
return None
else:
return None
def get_page_comment(self):
other_comments = self.get_comment_for_tv_or_movie()
paginator = Paginator(other_comments, self.page_num)
page = self.request.GET.get("page", 1)
try:
pages = paginator.page(page)
except PageNotAnInteger:
pages = paginator.page(1)
except EmptyPage:
pages = paginator.page(1)
return pages
def send_contexts_detail(self,form) -> dict:
data_for_tvor_movie = self.get_object_tv_or_movie_data()
recommendations_data = self.get_recommendations_tmdb_data()
for_movie_or_tv = self.obj_tv_or_movie.judge_tv_or_movie
mycomment_obj = self.get_user_post_comment_for_tv_or_movie()
average = self.obj_tv_or_movie.getaverage_stars()
pages = self.get_page_comment()
context = {
"data": data_for_tvor_movie,
"recommendations": recommendations_data,
"type": for_movie_or_tv,
"mycomment": mycomment_obj,
"average": average,
"form": form,
"pages": pages, # NOTE add the comment to context
}
return context
tv_or_movie before saving i.e.
if not Comment.objects.filter(user=request.user, tv_or_movie=tv_or_movie_object).exists()
Comment(comment=form.cleaned_data["comment"], user=request.user, stars=form.cleaned_data["stars"], tv_or_movie=tv_or_movie_object).save()
Related
I am using django-simple-history to log changes in my models. I have managed to save log of changes, but I always get None on field history_user.
I am following this tutorial.
This is my model:
class Reservation(models.Model):
UNCONFIRMED = 'UNCONFIRMED'
CONFIRMED = 'CONFIRMED'
CANCELED = 'CANCELED'
NO_SHOW = 'NO SHOW'
GO_SHOW = 'GO SHOW'
STATUS_CHOICES = (
(UNCONFIRMED, _('Unconfirmed')),
(CONFIRMED, _('Confirmed')),
(CANCELED, _('Canceled')),
(NO_SHOW, _('No show')),
(GO_SHOW, _('Go show')),
)
booking = models.CharField(max_length=25, verbose_name=_('Booking'))
agency = models.ForeignKey(Agency, on_delete=models.PROTECT, related_name='reservations', verbose_name=_('Agency'))
comment = models.TextField(null=True, blank=True, verbose_name=_('Comment'))
status = models.CharField(max_length=15, choices=STATUS_CHOICES, default=UNCONFIRMED, verbose_name=_('Status'))
confirmation_date = models.DateTimeField(null=True, blank=True, verbose_name=_("Confirmation Date"))
arrival_date = models.DateField(verbose_name=_('Arrival Date'))
departure_date = models.DateField(verbose_name=_('Departure Date'))
confirmation = models.CharField(max_length=15, null=True, blank=True, verbose_name=_('Confirmation Code'))
is_invoiced = models.BooleanField(default=False, verbose_name=_('Is invoiced?'))
euroamerica = models.BooleanField(default=False, verbose_name=_("Is Euroamerica sale"))
user = models.ForeignKey(User, null=True, blank=True, on_delete=models.PROTECT, related_name='reservations')
changed_by = models.ForeignKey(User, null=True, blank=True, on_delete=models.PROTECT)
timestamp = models.DateTimeField(null=True, blank=True, auto_now_add=True)
handle_fee = models.FloatField(null=True, blank=True, verbose_name=_("Handle Fee"))
log = HistoricalRecords(related_name='history')
class Meta:
verbose_name = _('Reservation')
verbose_name_plural = _('Reservations')
permissions = (
('reservation_can_edit', 'Can Edit Reservation'),
('reservation_can_view', 'Can View Reservation'),
('reservation_can_view_history_log', 'Can View Reservation History Log')
)
def __str__(self):
return "[{}]{}".format(self.booking, self.agency)
def clean(self, *args, **kwargs):
if self.id is None:
try:
Reservation.objects.get(booking=self.booking)
except:
pass
else:
raise CustomValidation(_('Booking already exists.'), 'booking', status_code=status.HTTP_400_BAD_REQUEST)
if isinstance(self.arrival_date, str):
raise CustomValidation(_('Arrival date must be a valid date.'), 'arrival_date', status_code=status.HTTP_400_BAD_REQUEST)
if isinstance(self.departure_date, str):
raise CustomValidation(_('Departure date must be a valid date.'), 'departure_date', status_code=status.HTTP_400_BAD_REQUEST)
if self.arrival_date >= self.departure_date:
raise CustomValidation(_('Departure date must be later than Arrival date.'), 'departure_date', status_code=status.HTTP_400_BAD_REQUEST)
# elif self.arrival_date <= timezone.datetime.now().date():
# if self.id == None:
# raise CustomValidation(_('Arrival date must be later than today.'), 'arrival_date', status_code=status.HTTP_400_BAD_REQUEST)
if self.status == 'CONFIRMED' and self.confirmation is None:
raise CustomValidation(_('Must provide a confirmation number.'), 'confirmation', status_code=status.HTTP_400_BAD_REQUEST)
return True
def save(self, *args, **kwargs):
from GeneralApp.models import Parameter
self.booking = self.booking.replace(' ', '')
self.booking = self.booking.upper()
self.full_clean()
if self.confirmation is not None and self.status is not self.CONFIRMED:
if self.pk is not None:
try:
previous_reservation = Reservation.objects.get(id=self.pk)
except:
pass
else:
if previous_reservation.status == self.status and previous_reservation.confirmation != self.confirmation:
self.status = self.CONFIRMED
else:
self.status = self.CONFIRMED
if self.status == self.CONFIRMED and self.confirmation_date is None:
self.confirmation_date = timezone.now()
try:
self.handle_fee = Parameter.objects.get(name="Handle Fee").value
except:
self.handle_fee = None
super(Reservation, self).save(*args, **kwargs)
#property
def _history_user(self):
return self.changed_by
#_history_user.setter
def _history_user(self, value):
self.changed_by = value
I don't know what I am missing to get the user logged.
EDIT
Here is where I save Reservation:
def save_reservation(self, reservation_data, user, pk):
try:
reservation_to_save = models.Reservation.objects.get(booking=pk)
except:
raise CustomValidation(_("There is not such reservation {}".format(pk)), 'id', status.HTTP_400_BAD_REQUEST)
reservation_to_save.booking = reservation_data['booking']
reservation_to_save.agency = models.Agency.objects.get(id=reservation_data['agency'])
reservation_to_save.comment = reservation_data.get('comment', None)
reservation_to_save.status = reservation_data.get('status', 'UNCONFIRMED')
reservation_to_save.arrival_date = reservation_data['arrival_date']
reservation_to_save.departure_date = reservation_data['departure_date']
reservation_to_save.confirmation = reservation_data.get('confirmation', None)
reservation_to_save.is_invoiced = reservation_data.get('is_invoiced', False)
reservation_to_save.euroamerica = reservation_data.get('euroamerica', False)
reservation_to_save.save()
return reservation_to_save
According to the documentation you have to add HistoryRequestMiddleware to your middleware in settings.py
Like so:
MIDDLEWARE = [
# ...
'simple_history.middleware.HistoryRequestMiddleware',
]
This at least was the solution in my case.
If you don't want to use this middleware you can set the user manually, see this.
I have a new problem, it is when I insert in the database the news, it adds but without the region. How can I insert the comment with the correct region?
Here is my view :
def index_region(request,region):
try:
actuCommentaire = Actu.objects.get(region=region)
except ObjectDoesNotExist:
actuTempo = Actu(region=region)
actuCommentaire = actuTempo.commentaire
form = UpdateActu(request.POST or None, instance=actuCommentaire)
if form.is_valid():
form.save()
return redirect('index.html')
Here is my model "Actu" :
class Actu(models.Model):
commentaire = models.TextField(max_length=200, null=True)
region = models.CharField(max_length=30, null=True)
def __str__(self):
return self.region
Here is my form :
class UpdateActu(forms.ModelForm):
class Meta:
model = models.Actu
fields = ['commentaire']
widgets = {
'commentaire': forms.Textarea(attrs={'class': 'form-control', 'id': 'exampleTextarea', 'rows': '2'})
}
Here is the result when inserting into the database :
enter image description here
I found how to solve the problem.
Here is my new view :
try:
actuCommentaire = Actu.objects.get(region=region)
except ObjectDoesNotExist:
actuTempo = Actu(region=region)
actuCommentaire = actuTempo.commentaire
form = UpdateActu(request.POST or None, instance=actuCommentaire)
if form.is_valid():
if Actu.objects.filter(region=region).count() == 0:
formActu = Actu(commentaire=request.POST['commentaire'], region=region)
formActu.save()
else:
form.save()
return redirect('index.html')
Currently I am inspecting a django queries(django-rest-framework) with django debug toolbar. The postgres database query time is about 100ms which is ok. But the response time took about 6 sec., which is slow.
I found that the slowest(takes about 4 sec.) function is this:
#transaction.atomic
def create(self, *args, **kwargs):
response = super(PersonView, self).create(*args, **kwargs)
data = response.data
product_data = data['packages'][0]['product']
person = Person.objects.get(pk=data['id'])
if not person.info:
product = Product.objects.get(pk=product_data['id'])
PersonItem.objects.create(person=person, product=product)
response.data = PersonSerializer(person).data
return response
Any ideas why the response is so slow or why this function takes so long ?
Edit:
This is my PersonSerializer class.
class PersonSerializer(serializers.ModelSerializer):
person_items = PersonItemSerializer(many=True, read_only=True)
address = AddressSerializer(read_only=True)
office_address = serializers.SerializerMethodField('get_office_address')
status = ChoiceWithNameField(choices=Person.STATUS_CHOICES)
source = ChoiceWithNameField(
choices=Person.SOURCE_CHOICES, required=False)
closing_person = ClosingPersonSerializer(read_only=True)
is_upfront_person = serializers.BooleanField(read_only=True)
has_discount = serializers.BooleanField(read_only=True)
payment_method = serializers.CharField(read_only=True)
user_email = serializers.CharField(
read_only=True, source='user.email')
user_display_name = serializers.CharField(
read_only=True, source='user.display_name')
billing_display_address = serializers.SerializerMethodField(
'get_billing_display_address')
real_estate_display_address = serializers.SerializerMethodField(
'get_real_estate_display_address')
net_total = serializers.DecimalField(read_only=True, source='net_total')
gross_total = serializers.DecimalField(read_only=True, source='gross_total')
tax = serializers.DecimalField(read_only=True, source='tax_total')
coupon_net_total = serializers.DecimalField(read_only=True,
source='coupon_net_total')
coupon_gross_total = serializers.DecimalField(read_only=True,
source='coupon_gross_total')
class Meta:
model = Person
policy_class = PersonPolicy
def get_billing_display_address(self, obj):
try:
address = obj.address
except Address.DoesNotExist:
pass
else:
return "{0} {1}, {2}, {3}".format(
address.house_name_number, address.address_2, address.town,
address.postcode)
def _filter_products(self, products):
user = getattr(self.context.get('request'), 'user', None)
if not user or not user.is_staff:
products = products.filter(admin_only=False)
return products
def _as_person_items(self, person, products):
persons = [PersonItem(name=p.name, description=p.description,
person=person,
price=p.total_up_front,
product=p) for p in products]
return persons
def get_packages(self, person):
products = self._filter_products(person.packages)
return PersonItemMinimalSerializer(
self._as_person_items(person, products),
many=True).data
def get_office_address(self, i):
try:
office_address = i.addresses.get(address_type=Address.ADDRESS)
except Address.DoesNotExist:
return False
return AddressSerializer(office_address, read_only=True).data
def get_real_estate_display_address(self, obj):
try:
address = obj.office_address
except Address.DoesNotExist:
pass
else:
return "{0} {1}, {2}, {3}".format(
address.house_name_number, address.address_2,
address.town, address.postcode)
to count about multiply model with another model in foreignkey but in different app, here the penjualan app and detail_penjualan_obat model :
class detail_penjualan_obat(models.Model):
kd_penjualan_detail = models.ForeignKey(penjualan_obat)
kd_obat_detail = models.ForeignKey(obat)
jumlah_jual = models.IntegerField()
total_harga_perobat = MoneyField(max_digits=10, decimal_places=2, default_currency='IDR')
def __unicode__(self):
return self.total_harga_perobat
and the obat app model :
class obat(models.Model):
jenis_obat = {
('obat bebas','Obat Bebas'),
('obat bebas terbatas','Obat Bebas Terbatas'),
('obat keras dan psikotropika','Obat Keras dan Psikotropika')
}
kd_obat = models.CharField(primary_key = True, default = '', max_length = 10, validators=[RegexValidator(r'^\d{1,10}$')])
nama_obat = models.CharField(max_length = 15)
tipe_obat = models.CharField(max_length = 35, choices = jenis_obat)
harga_jual = MoneyField(max_digits=10, decimal_places=2, default_currency='IDR')
def __unicode__(self):
return self.kd_obat
and the view.py :
def data_penjualan_obat_detail(request):
if request.method == 'POST':
form_data = request.POST
form = penjualan_detail_form(form_data)
if form.is_valid():
input_detail_penjualan = detail_penjualan_obat(
kd_penjualan_detail = form.cleaned_data.get('kd_penjualan_detail'),
kd_obat_detail = form.cleaned_data.get('kd_obat_detail'),
jumlah_jual = request.POST['jumlah_jual'],
total_harga_perobat = form.cleaned_data.get('total_harga_perobat')
)
input_detail_penjualan.save()
return redirect('/')
else:
form = penjualan_detail_form()
return render(request, 'penjualan_detail.html',{'form':form})
here the function :
total_harga_perobat = harga_jual * jumlah_jual
and that function how to be 'place and work' in the view ?
Sorry I have bad english but I hope you will understand what about my question, and solve my problem
EDIT :
here model of penjualan_obat :
class penjualan_obat(models.Model):
kd_penjualan = models.CharField(primary_key = True, default = '', max_length = 10, validators=[RegexValidator(r'^\d{1,10}$')], unique = True)
kd_costumer_obat = models.ForeignKey(costumer)
tgl_penjualan = models.DateField(auto_now_add = True)
total_penjualan = MoneyField(max_digits=10, decimal_places=2, default_currency='IDR')
def __unicode__(self):
return self.kd_penjualan
and the form penjualan_detail_form :
class penjualan_detail_form(ModelForm):
class Meta:
model = detail_penjualan_obat
fields = ['kd_penjualan_detail','kd_obat_detail','jumlah_jual']
labels = {
'kd_penjualan_detail' : 'Kode Penjualan',
'kd_obat_detail' : 'Kode Obat',
'jumlah_penjualan' : 'Jumlah Penjualan',
}
error_messages = {
'kd_penjualan_detail':{
'required':'Anda harus memilih kode penjualan'
},
'kd_obat_detail':{
'required':'Anda harus memilih kode obat'
},
'jumlah_penjualan':{
'required':'Anda harus mengisi jumlah penjualan'
},
}
CORRECTIONS
I suggest you to change model class name with standard naming.
an example class detail_penjualan_obat(models.Model) to class DetailPenjualanObat(models.Model)
and I suggest you to not using default = '' to your unique field.
This will be difficult whenever you migrating the databases.
Why you implement to the form form.cleaned_data.get('total_harga_perobat'),
if you actually want to save it with automatically? you should handle it at your backend.
I checked at your source code, and your penjualan_detail_form
already instanced to the model of detail_penjualan_obat. perhaps like this below;
def data_penjualan_obat_detail(request):
if request.method == 'POST':
form = penjualan_detail_form(request.POST)
if form.is_valid():
initial = form.save(commit=False)
initial.total_harga_perobat = initial.kd_obat_detail.harga_jual * initial.jumlah_jual
initial.save()
form.save()
return redirect('/')
else:
form = penjualan_detail_form()
return render(request, 'penjualan_detail.html', {'form': form})
Hope it can help..
UPDATE
You also can handle it in your models.py, an example:
class DetailPenjualanObat(models.Model):
kd_penjualan_detail = models.ForeignKey(penjualan_obat)
kd_obat_detail = models.ForeignKey(obat)
jumlah_jual = models.IntegerField()
total_harga_perobat = MoneyField(max_digits=10, decimal_places=2, default_currency='IDR')
def save(self, *args, **kwargs):
self.total_harga_perobat = self.kd_obat_detail.harga_jual * self.jumlah_jual
super(DetailPenjualanObat, self).save(*args, **kwargs)
See also about pre save, like this answer
I faced the error when I tried to capture the POST data from a form. Weird, because the same algorithm works with another django app model.
The models:
class Item(models.Model):
code = models.CharField(max_length=200, unique=True)
barcode = models.CharField(max_length=300)
desc = models.CharField('Description',max_length=500)
reg_date = models.DateField('registered date')
registrar = models.CharField(max_length=100)
def __unicode__(self):
return self.code + ' : ' + self.desc
class ItemInfo(models.Model):
model = models.ForeignKey(Item)
supplier = models.ForeignKey(Supplier)
stock_on_hand = models.IntegerField()
stock_on_order = models.IntegerField()
cost = models.IntegerField()
price = models.IntegerField()
unit = models.CharField(max_length=100)
lead_time = models.IntegerField()
def __unicode__(self):
return Item.code + ' : ' + supplier
class ItemForm(ModelForm):
class Meta:
model = Item
class ItemInfoForm(ModelForm):
class Meta:
model = ItemInfo
exclude = ('model')
And the views.py function for non-working (Item) is like this:
def register(request):
csrf_context = RequestContext(request)
current_user = User
if request.user.is_authenticated():
if request.POST:
item = Item()
item_info = ItemInfo()
header_form == ItemForm(data=request.POST,instance=item)
details_form == ItemInfoForm(data=request.POST, instance=item_info)
if header_form.is_valid():
header = header_form.save()
if details_form.is_valid():
details = details_form.save(commit=False)
details.supplier = header
details.save()
return HttpResponseRedirect('/item/')
else:
return render_to_response('error/denied_data_entry.html')
else:
header_form = ItemForm()
details_form = ItemInfoForm()
return render_to_response('item/register.html',{'header_form' : header_form, 'details_form' : details_form}, csrf_context)
else:
return render_to_response('error/requires_login.html', csrf_context)
The working views.py function for another working (Supplier) model is here:
def register(request):
csrf_context = RequestContext(request)
current_user = User
if request.user.is_authenticated():
if request.POST:
supplier = Supplier()
supplier_info = SupplierInfo()
header_form = SupplierForm(data=request.POST, instance=supplier)
details_form = SupplierInfoForm(data=request.POST, instance=supplier_info)
if header_form.is_valid():
header = header_form.save()
if details_form.is_valid():
details = details_form.save(commit=False)
details.model = header
details.save()
return HttpResponseRedirect('/supplier/')
else:
return render_to_response('error/denied_data_entry.html')
else:
return render_to_response('error/denied_data_entry.html')
else:
header_form = SupplierForm()
details_form = SupplierInfoForm()
return render_to_response('supplier/register.html', {'header_form' : header_form, 'details_form' : details_form}, csrf_context)
else:
return render_to_response('error/requires_login.html', csrf_context)
The traceback page shows that the POST did pass some variable. Help me please, I cant figure it out why it works on Supplier, but not Item.
P/S: Sorry for the indentation.
The problem is here:
# ...
header_form == ItemForm(data=request.POST,instance=item)
details_form == ItemInfoForm(data=request.POST, instance=item_info)
You're not assigning, you're comparing.