Django REST throwing a 404 error on DetailView - django

I am trying to create a RetrieveUpdateDestroyAPIView in my DJango REST application but I get a 404 error when I try to visit the URL.
Here is urlpattern in my urls.py file which maps to the views.py file below
re_path(r'^snippets/(?P<username>.+)/(?P<snippet_id>\d+)/$',
views.SnippetDetailView.as_view(), name="snippet-detail"),
Here is my views.py code.
class SnippetDetailView(generics.RetrieveUpdateDestroyAPIView):
"""
Retrieve, update or delete a snippet.
"""
permission_classes = [IsOwnerOrReadOnly]
serializer_class = SnippetSerializer
lookup_url_kwarg = 'snippet_id'
def get_queryset(self):
self.user = get_object_or_404(User, username=self.kwargs['username'])
return Snippet.objects.filter(owner__username=self.user.username)
And this is my models.py code
class Snippet(models.Model):
owner = models.ForeignKey('auth.User', related_name='snippets',
on_delete=models.CASCADE)
highlighted = models.TextField() # to store representation of code
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, default="", blank=True)
code = models.TextField()
linenos = models.BooleanField(default=False)
language = models.CharField(choices=LANGUAGE_CHOICES, default='python',
max_length=100)
style = models.CharField(choices=STYLE_CHOICES, default='friendly',
max_length=100)
class Meta:
ordering = ["created"]
I've been trying to understand what could be wrong with it but am relatively new and I have no idea as to why this is not working. Looked up the documentation and a few examples but I don't see any one of them having multiple named parameters in their urls.py file. I would appreciate any help a lot.

Related

Django Rest Framework - overriding save in backend is not creating custom id

I am working on a project. I have Django for my backend and Vue for my front end. When using the templates and saving in the Django project I have no issues.
However, when I POST to my projects API the following save from my modal is not being created.
models.py
class DevProjects(models.Model):
PROJECT_TYPE = [
('New Application', 'New Application'),
('Update Application', 'Update Application'),
('Bug Fixes', 'Bug Fixes')
]
PROJECT_STATUS = [
('New', 'New'),
('In Progress', 'In Progress'),
('Complete', 'Complete'),
]
project_id = models.CharField(max_length=15, editable=False, unique=True)
project_title = models.CharField(max_length=100)
project_desc = models.CharField(max_length=500)
project_category = models.CharField(max_length=25, choices=PROJECT_TYPE, null=True, blank=True)
project_status = models.CharField(max_length=25, choices=PROJECT_STATUS, default='New')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
created_by = models.ForeignKey(User, related_name='projects', on_delete=models.CASCADE)
def save(self, *args, **kwargs):
super(DevProjects, self).save(**kwargs)
self.project_id = 'PROJ-' + str(self.id)
super(DevProjects, self).save(**kwargs)
def __str__(self):
return self.project_title
I have the project_id being created on save which gets the original ID but just adds 'PROJ-' in front. Whenever I submit the form from my frontend, that save definition is not being called, thus not creating the project_id.
Project ID is what I use to send a GET request to get the projects.
serailizers.py
class DevProjectSerializer(serializers.ModelSerializer):
class Meta:
model = DevProjects
fields = ("project_id", "project_title", "project_desc", "project_category", "project_status")
views.py
class DevProjectViewSet(viewsets.ModelViewSet):
serializer_class = DevProjectSerializer
queryset = DevProjects.objects.all()
def perform_create(self, serializer):
serializer.save(created_by=self.request.user)
Whenever I post, I get the following error:
IntegrityError: UNIQUE constraint failed: ckcSupportWeb_devprojects.project_id
What do I need to do for the project_id to generate when POSTing from DRF? Any and all help is appreciated.
UPDATE
I can try to use the following code in my viewset:
def create(self, *args, **kwargs):
self.project_id = 'PROJ-' + str(self.id)
super(DevProjectViewSet, self).save(**kwargs)
But, I get the following error:
self.project_id = 'PROJ-' + str(self.id)
AttributeError: 'DevProjectViewSet' object has no attribute 'id'
I am truly stuck on how to handle this for API post requests.
From what I can understand, you can relax the unique constraint on the project_id and your previous code should work fine.
Since the project code is not editable, it won't be updated from a POST API call.
I was able to get rid of all of these issues with writing a simple function in utils.py that gets the latest ID created, adds 1 and then sets the new project_id.
Try with this code snippet
def save(self, *args, **kwargs)
self.project_id = 'PROJ-' + str(self.id)
super(DevProjects, self).save(**kwargs)

Detected path traversal attempt - Django/Heroku(Bucketeer)

I'm getting this error when trying to upload using FileField. I'm using Bucketeer on Heroku to upload to an AWS bucket. I've seen a few threads on this issue but haven't been able to figure it out.
The file upload view:
class UploadTicketAttachment(APIView):
permission_classes = []
parser_classes = (MultiPartParser, FormParser)
def post(self, request, format=None):
user = request.user
serializer = AttachmentSerialiazer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.validated_data['uploaded_by'] = user
serializer.save()
return Response(serializer.data['id'])
else:
return Response(f'{serializer.errors}, attachment upload failed')
The model:
class Attachment(models.Model):
file = models.FileField(upload_to="/ticket_attachments", blank=True, null=True)
created_on = models.CharField(max_length=20, null=True)
uploaded_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, related_name="uploaded_by")
parent_ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, null=True, related_name="attachment")
def __str__(self):
return self.file.name
For the settings/bucketeer configuration I followed which uses django-storages:
https://dev.to/heroku/properly-managing-django-media-static-files-on-heroku-o2l
I don't think the issue is on that end since I set it up the exact same way in another project and it works fine with the only difference being that the other project uses ImageField instead of FileField.
Django version is 4.0.2. Any ideas? Thanks

How to run two methods inside django models in parallel manner?

I'm incrementing two different fields in User models which are the number of likes given to user's post and number of likes received by the user's post. I did this through adding two methods in class model. It is working when I'm liking other posts but however, if I liked my own post it is not working. Anyone know how to solve this problem?
Code snippet for user models
class User(models.Model):
username = models.CharField(max_length=100)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
email = models.EmailField(null=True)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
datetime_joined = models.DateTimeField(auto_now_add=True)
num_likes_received = models.IntegerField(default=0)
num_dislikes_received = models.IntegerField(default=0)
num_likes_given = models.IntegerField(default=0)
num_dislikes_given = models.IntegerField(default=0)
total_votes = models.IntegerField(default=0)
prediction = models.BooleanField(default=True)
def add_total_votes(self, vote):
self.total_votes += vote
self.save()
def inc_num_likes_received(self):
self.num_likes_received += 1
print("1 like received")
self.save()
def inc_num_likes_given(self):
self.num_likes_given += 1
print("1 like given")
self.save()
Code snippet for Post Model
class Post(models.Model):
post = models.TextField()
num_likes = models.IntegerField(default=0)
num_dislikes = models.IntegerField(default=0)
username = models.ForeignKey(User, on_delete=models.CASCADE)
datetime_comments = models.DateTimeField(auto_now_add=True)
def add_num_likes(self):
self.num_likes+=1
self.save()
def __str__(self):
return str(self.id)
Function for submitting like to certain post and at the same time this is where I executed the created two method from the models. (inc_num_likes_received and inc_num_likes_given)
def submit_like(request):
User_PK = User.objects.get(username=request.user)
User_Give_Like = get_object_or_404(User, pk=User_PK.id)
pk = request.POST.get('Status', None)
post = get_object_or_404(Post, pk=pk)
post.add_num_likes()
User_PK_Receive = User.objects.get(id=post.username.id)
User_Receive_Like = get_object_or_404(User, pk=User_PK_Receive.id)
LikeObject = Likes(username = User_Give_Like, post = post, liked=True, disliked=False)
LikeObject.save()
User_Give_Like.inc_num_likes_given()
User_Receive_Like.inc_num_likes_received()
return HttpResponse(200)
To become more specific these two lines of code below are the methods. The problem here is that the first line of code are not actually working but the last line are the only working. Whenever I rearranged these two lines of code the only executed was the last line of code.
....
User_Give_Like.inc_num_likes_given()
User_Receive_Like.inc_num_likes_received()
Example:
In this case, User_Receive_Like.inc_num_likes_given() are working but User_Receive_Like.inc_num_likes_received() are not working and vice-versa.
....
User_Receive_Like.inc_num_likes_received()
User_Give_Like.inc_num_likes_given()
The expected output is both fields num_likes_given and num_likes_received must incremented by 1.

Object-level permissions in Django

I have a ListView as follows, enabling me to loop over two models (Market and ScenarioMarket) in a template:
class MarketListView(LoginRequiredMixin, ListView):
context_object_name = 'market_list'
template_name = 'market_list.html'
queryset = Market.objects.all()
login_url = 'login'
def get_context_data(self, **kwargs):
context = super(MarketListView, self).get_context_data(**kwargs)
context['scenariomarkets'] = ScenarioMarket.objects.all()
context['markets'] = self.queryset
return context
The two market models are as follows:
class Market(models.Model):
title = models.CharField(max_length=50, default="")
current_price = models.DecimalField(max_digits=5, decimal_places=2, default=0.50)
description = models.TextField(default="")
shares_yes = models.IntegerField(default=0)
shares_no = models.IntegerField(default=0)
b = models.IntegerField(default=100)
cost_function = models.IntegerField(default=0)
open = models.BooleanField(default=True)
def __str__(self):
return self.title[:50]
def get_absolute_url(self):
return reverse('market_detail', args=[str(self.id)])
class ScenarioMarket(models.Model):
title = models.CharField(max_length=50, default="")
description = models.TextField(default="")
b = models.IntegerField(default=100)
cost_function = models.IntegerField(default=0)
most_likely = models.CharField(max_length=50, default="Not defined")
open = models.BooleanField(default=True)
def __str__(self):
return self.title[:50]
def get_absolute_url(self):
return reverse('scenario_market_detail', args=[str(self.id)])
And my user model is as follows:
class CustomUser(AbstractUser):
points = models.DecimalField(
max_digits=20,
decimal_places=2,
default=Decimal('1000.00'),
verbose_name='User points'
)
bets_placed = models.IntegerField(
default=0,
verbose_name='Bets placed'
)
net_gain = models.DecimalField(
max_digits=20,
decimal_places=2,
default=Decimal('0.00'),
verbose_name='Net gain'
)
class Meta:
ordering = ['-net_gain']
What I want happen is that different users see different sets of markets. For example, I want users from company X to only see markets pertaining to X, and same for company Y, Z, and so forth.
Four possibilities so far, and their problems:
I could hardcode this: If each user has a company feature (in addition to username, etc.), I could add a company feature to each market as well, and then use if tags in the template to ensure that the right users see the right markets. Problem: Ideally I'd want to do this through the Admin app: whenever a new market is created there, it would be specified what company can see it.
I could try to use Django's default permissions, which of course would be integrated with Admin. Problem: Setting a view permission (e.g., here) would concern the entire model, not particular instances of it.
From googling around, it seems that something like django-guardian might be what I ultimately have to go with. Problem: As I'm using a CustomUser model, it seems I might run into problems there (see here).
I came across this here on SO, which would enable me to do this without relying on django-guardian. Problem: I'm not clear on how to integrate that into the Admin app, in the manner that django-guardian seems able to.
If anyone has any advice, that would be greatly appreciated!
You can add some relationships between the models:
class Company(models.Model):
market = models.ForeignKey('Market', on_delete=models.CASCADE)
...
class CustomUser(AbstractUser):
company = models.ForeignKey('Company', on_delete=models.CASCADE)
...
then in your view you can simply filter the queryset as appropriate:
class MarketListView(LoginRequiredMixin, ListView):
context_object_name = 'market_list'
template_name = 'market_list.html'
login_url = 'login'
def get_queryset(self):
return Market.objects.filter(company__user=self.request.user)
Note, you don't need the context['markets'] = self.queryset line in your get_context_data; the queryset is already available as market_list, since that's what you set the context_object_name to.

Simple django DetailView doesn't work, how to pass to pass 2 arguments to DetailView

I want to create detail view for my Playlist model. I followed steps from docs and i keep getting this error:
AttributeError at /root
Generic detail view PlaylistDetail must be called with either an object pk or a slug in the URLconf.
here is my code:
model:
class Playlist(models.Model):
title = models.CharField(max_length=40, null=True)
description = models.CharField(max_length=500, null=True)
author = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
miniature = models.ImageField(upload_to='images/playlist', default="defaults/default.png", validators=[validate_miniature_file_extension])
tracks = models.ManyToManyField(Track)
def __str__(self):
return self.title
url:
path('<slug:author>', PlaylistDetail.as_view(), name='playlist'),
view:
class PlaylistDetail(DetailView):
model = Playlist
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['now'] = timezone.now()
return context
I suppose its caused because of that there are more than one playlist created by the same user, and it will must get User && title. Any suggestions?
The issue is that in the urls, you use <slug:author>
path('<slug:author>', PlaylistDetail.as_view(), name='playlist'),
And by default it looks for slug.
Try updating the view with this:
class PlaylistDetail(DetailView):
model = Playlist
slug_url_kwarg = 'author' # <----
def get_context_data(self, **kwargs):
# . . .