I have a code that allows user to like the post but I realized the user can like the post more than once which I don't want. How do I restrict this?
my code
#login_required
def like_post(request, pk):
if pk:
liked_post = Post.objects.get(id=pk)
count = liked_post.likes
count += 1
liked_post.likes = count
liked_post.save()
return redirect('/community/post/%s' %liked_post.id)
what I tried
adding something like this....but not sure
if post.likes.filter(id=user.id).exists():
post.likes.remove(user)
else:
post.likes.add(user)
You can use the unique_together meta class option. This would raise a validation error which you could pass over if you didn't want to report an error.
In your Likes model (if you have one) include something like the following:
class Meta():
unique_together = ('id', 'user')
Or add a 'liked_by' attribute to Post as a many to many field to users which should manage the uniqueness stuff for you. The relationship can only exist once. You could use Post.liked_by.count() to get the number of likes, but I'd have no issue seeing the like count as its own attribute.
Related
There are two models .I want to make query to extract only the app exact app related Adspaces .
models.py
class Appname(models.Model):
user=models.ForeignKey(User,related_name='appname', null=True, default=None,on_delete=models.CASCADE)
name=models.CharField(max_length=150,blank=False,null=False,help_text='Add your new App')
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("dashapp:space",kwargs={'pk':self.pk})
class Adspace(models.Model):
user=models.ForeignKey(User,related_name='adspace', null=True, default=None,on_delete=models.CASCADE)
ad_space=models.CharField(max_length=150,blank=False,null=False)
app=models.ForeignKey('Appname', related_name='appnames',default=None, on_delete=models.CASCADE)
PID_TYPE = (
('FN','FORMAT_NATIVE'),
('FNB','FORMAT_NATIVE_BANNER'),
('FI','FORMAT_INTERSTITIAL'),
('FB','FORMAT_BANNER'),
('FMR','FORMAT_MEDIUM,RECT'),
('FRV','FORMAT_REWARDED_VIDEO'),
)
format_type=models.CharField(max_length=3,choices=PID_TYPE,default='FN',blank=False, null=False)
def __str__(self):
return self.ad_space
def get_absolute_url(self):
return reverse("dashapp:create",kwargs={'pk':self.pk})
Views.py
SHowing the one where i need to the query
class spacelist(LoginRequiredMixin,ListView):
model=Adspace
template_name='adspace_list.html'
def get_queryset(self):
query_set=super().get_queryset()
return query_set.filter(user=self.request.user)
Here I need to perform One more query so that EACH APP show their own adspaces when clicked right now every app show every show adspaces.
I have the idea what to do as if i compare app_id then it'll show the exact app related adspaces, but i dont know how to write query for the same as i already have one query present.???
You could try using a Q objects: https://docs.djangoproject.com/en/2.1/topics/db/queries/#complex-lookups-with-q-objects
From what I understand you are trying to filter both on the app_id and the request user at the same time, so you could try look something like this:
from django.db.models import Q
...
def get_queryset(self):
query_set=super().get_queryset()
return query_set.filter(Q(user=self.request.user) & Q(app_id=app_id))
...
This lets you do a single filter with both your requirements at the same time (i.e. retrieve the Adspace instances for a specific user with a specific Appname).
You chain another filter at the end like this:
class spacelist(LoginRequiredMixin,ListView):
model=Adspace
template_name='adspace_list.html'
def get_queryset(self):
query_set = super().get_queryset()
query_set = query_set.filter(user=self.request.user)
app_id = [...]
return query_set.filter(app_id=app_id)
The problem left is to find out what is the app_id coming from. How do you know what is the current app? Several options here.
Option 1: From the request
It can come from the current user: self.request.user.appname.all() but that will give you multiple apps, if the user can only have one app, you should change your model Appname.user to a OneToOneField.
Otherwise, I suggest changing your related_name='appnames' to reflect the multiplicity in the reverse relationship.
Option 2: From the URL
It can come from the URL, your space list view should extract an app_id parameter from the URL where it's defined:
url(r'^(?P<app_id>[0-9]+)/spaces/$', spacelist.as_view(), name='space_list'),
And then in the spacelist view, you would get this parameter like this:
app_id = self.kwargs['app_id']
return query_set.filter(app_id=app_id)
Hope that helps
UPDATE:
Also worth noting that QuerySets are lazy, meaning the result will get evaluated as late as possible by Django. Therefore, when you call:
query_set = query_set.filter(user=self.request.user)
The Django ORM doesn't execute any DB queries yet, and you can chain more filters after that:
query_set = query_set.filter(user=self.request.user)
query_set = query_set.filter(app_id=app_id)
Which behind the scenes is extending the query that will be executed when required. But at this point, no query is actually run. To see the query that will get executed you can print out the query attribute of the QuerySet:
print(query_set.query)
Which should log something like:
SELECT "app_adspace"."user_id" ...
FROM
"app_adspace"
WHERE
"app_adspace"."user_id" = 1234 AND "app_adspace"."app_id" = 5678
Let's assume I have a model like this:
class Data(models.Model):
a = models.CharField()
b = models.CharField()
c = models.IntegerField()
I would like to setup a serializer in such a way that it automatically fills in field c and it is not required for a POST. I tried to overwrite the create function of the serializer, but it doesn't work:
class DataSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Data
fields = ('a', 'b')
def create(self, validated_data, **kwargs):
Data.objects.c = 5
return Data.objects.create(**validated_data)
However, if I try this, I end up with an IntegrityError: NOT NULL constraint failed: model_data.c. What is the syntax that I have to use here?
EDIT: Updated formatting.
The reason you're getting the error because field c is not set to null = True - as such an error is raised at the validation stage even before the serializer hits the create method.
Bear in mind that the process goes like this:
Submit serializer data
field-level validation happens - this includes checks for null integrity, min/max length etc and also any custom field validations defined in def validate_<field_name>
object-level validation happens - this calls the def validate method
validated data is passed to the save method, depending on how you designed the serializer - it will save the instance, or route the data to either create or update
All of the info regarding this can be found in Django's and DRF's docs.
A few things to consider:
are you setting a global default for that field? If so, set the default in your models - c = models.IntegerField(default=a_number_or_a_callable_that_returns_an_integer)
do you intend to display the field? If so, include c in your fields and add one more Meta attribute - read_only_fields = ('c',)
If it's neither of the above, you might want to override the validate_c method
Apologies for the poor formatting, typing it on my phone - will update once I get to a computer
In your code Data.objects.c = 5 does nothing.
If you want to set this value yourself use validated_data['c'] = 5 or Data.objects.create(c=5, **validated_data) (just not both at the same time).
Rather than doing this in the serializer, there are hooks in the generic views that allow you to pass values to the serializer. So in your case you might have:
class DataViewSet(ModelViewSet):
# ...
def perform_create(self, serializer):
serializer.save(c=5)
See the "Save and deletion hooks" section here
i am a beginner to Django i am trying to use permissions to permit access to specific view functions via a decorator, for specific user types only. Right now i am totaly confused by all kinds of stuff i have read about and seam not to figure out how i should do this.
I have two different kinds of users let it be UserTypeONE and UserTypeTWO.
UserTypeONE and UserTypeTWO should have access to specific views only.
Here is my code:
myuserTypes.py
class UserTypeONE(models.Model):
lieOtO_User = models.OneToOneField(settings.AUTH_USER_MODEL)
lie_SomeAttribute= models.CharField(max_length=300, help_text ='Name')
class Meta:
permissions = (('Can_View_MyShop', 'Can see Shop View'),)
class UserTypeTWO(models.Model):
lieOtO_User = models.OneToOneField(settings.AUTH_USER_MODEL)
lie_SomeOtherAttribute= models.CharField(max_length=300, help_text ='Name')
class Meta:
permissions = (('Can_View_Targets', 'Can see the Targets'),)
Here is what i am trying to do in my views.py
#login_required
#permission_required('UserTypeONE.Can_View_MyShop', raise_exception=True)
def MyShopView(request):
#do something
i also tried
#user_passes_test(lambda u: u.usertypeone.permission('Can_View_MyShop'))
As you guys can see i am an absolute beginner unfortunately all documentations and examples havent done me any good instead i am even more confused.
I would really appreciate help on this.
I would use user_passes_test() here since you specifically want to restrict specific views.
First, define a couple of functions that return True when you're dealing with a user who should be able to see your content. It looks like your UserTypeOne and UserTypeTwo models extend the base User model with a one-to-one relationship, so you can use hasattr to check if a given base user has one of those attributes:
def type_one_only(user):
if hasattr (user, 'usertypeone'):
return True
else:
return False
def type_two_only(user):
#same thing without if/else
return hasattr(user, 'usertypetwo')
Now when you have a view that you want to restrict to one user type, you can add a user_passes_test decorator before it:
#user_passes_test(type_one_only, login_url='/')
def my_view(request):
...
login_url is where a user will be sent if they do not pass the test you've indicated.
I have two tabels(Ingredient_Step and Ingredient) in on relation as you can see below:
Models.Py
class Ingredient_Step(models.Model):
ingredient = models.ForeignKey(Ingredient)
Step = models.ForeignKey(Step)
def __unicode__(self):
return u'{}'.format(self.Step)
class Ingredient(models.Model):
IngredientName = models.CharField(max_length=200,unique=True)
Picture = models.ImageField(upload_to='Ingredient')
def __unicode__(self):
return u'{}'.format(self.IngredientName)
In a function, i need serialize a JSON object from a query that returns from "Ingredient_step", but I need send the field "IngredientName", who comes from "Ingredient" table.
I try using "ingredient__IngredientName" but it fails.
Views.Py:
def IngredientByStep(request):
if request.is_ajax() and request.GET and 'id_Step' in request.GET:
if request.GET["id_Step"] != '':
IngStp = Ingredient_Step.objects.filter(Step =request.GET["id_Step"])
return JSONResponse(serializers.serialize('json', IngStp, fields=('pk','ingredient__IngredientName')))
How i can call extends field from a relation?
Thanks
This "feature" of Django (and many ORM's like SQLAlchemy) are called Lazy Loading, meaning data is only loaded from related models if you specifically ask for them. In this case, build your IngStp as a list of results, and make sure to access the property for each result before serializing.
Here's an example of how to do that: Django: Include related models in JSON string?
Suppose I got two models like this:
class Article(models.Model):
article_title = models.CharField(max_length=100)
class EventRecord(models.Model):
article = models.ForeignKey(Article)
In a view, I select a certain EventRecord and want to show the Title of the Article it is related to as well. The following does not work:
def classify(request, pk):
event = get_object_or_404(EventRecord, pk=pk)
article_id = event.article
article = get_object_or_404(Article, pk=article_id)
How do I make this work?
Any help is really appreciated!
Django automatically handles this for you. For example:
>>> record = EventRecord.objects.get(...)
>>> isinstance(record.article, Article)
True
>>> record.article.article_title
u'title here'
This is one of the magical things Django does (nothing is magic but anyway...). Please keep in mind that in order for this work Django will usually execute some extra database queries. To eliminate them, you can use select_related method. Below is a snippet which eliminates extra queries and does what you want:
def classify(request, pk):
record = EventRecord.objects.filter(pk=pk).select_related()
# the above returns queryset hence you have to extract the record manually
if not len(record):
raise Http404()
else:
record = record[0]
# now use record as usual and no extra queries will be executed
title = record.article.article_title
...
event.article returns the actual Article object, not the primary key, so you don't need to do another database query.
def classify(request, pk):
event = get_object_or_404(EventRecord, pk=pk)
if not event.article:
raise Http404
print event.article.article_title