I´m trying to get a context_dictionary from specific urls to use in a django command. Code without the command returns the model with a slug and details to a site, let´s suppose it is localhost/event/mainevent/.
Once I want to get the context dictionary used for that site,how do I get it in my command?
My code so far is the following:
Commands/MyCommand.py
class Command(BaseCommand):
help = 'Closes the specified poll for voting'
def handle(self, *args, **options):
#Get context processor here
self.stdout.write(event.title)
Models.py
class Event(models.Model):
title = models.CharField(max_length=255, unique=True)
location = models.CharField(max_length=300)
slug = models.SlugField()
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Event, self).save(*args, **kwargs)
def __unicode__(self):
return self.title
Views.py
def details(request, event_title_slug):
context_dict = {}
try:
event = Event.objects.get(slug=event_title_slug)
context_dict['event_title'] = event.title
context_dict['event'] = event
except Event.DoesNotExist:
pass
return render(request, 'event_details.html', context_dict)
Urls.py
url(r'^event/(?P<event_title_slug>[\w\-]+)/$', views.details, name='details')
I don't understand what you mean by context dictionary here. The context in a view is for rendering a template; you don't want to do that in your command. And in any case you don't have a URL in the command, so there is no relationship to the view.
Instead you need to pass an argument, say the slug, to your command, and use that to get the event to delete. The documentation has an example that does almost exactly what you want.
Related
I'm creating a twitter-like app and I'm stuck on creating a UserProfileView which is supposed to display a certain User's profile, along with a list of posts made by that user below. Though I can't really figure out a way to create a proper view for that.
I'm trying to use class based views for that, the one I'll be inheriting from is probably DetailView (for profile model) and something inside of that which retrieves a queryset of posts made by that user:
My profile model looks like this:
class Profile(models.Model):
user = models.OneToOneField(
User, on_delete=models.CASCADE, primary_key=True)
display_name = models.CharField(max_length=32)
profile_picture = models.ImageField(
default='assets/default.jpg', upload_to='profile_pictures')
slug = models.SlugField(max_length=150, default=user)
def get_absolute_url(self):
return reverse("profile", kwargs={"pk": self.pk})
Post model:
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
date_posted = models.DateField(auto_now_add=True)
content = models.TextField(max_length=280)
image = models.FileField(upload_to='post_images/', blank=True, null=True)
def __str__(self) -> str:
return f'Post by {self.author} on {self.date_posted} - {self.content[0:21]}'
def get_absolute_url(self):
return reverse("post-detail", kwargs={"pk": self.pk})
I've tried creating this method:
class UserProfileView(DetailView):
model = Profile
context_object_name = 'profile'
template_name = 'users/profile.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user_posts'] = Post.objects.filter(author=Profile.user)
return context
But this one sadly doesn't work, raising an error of
"TypeError: Field 'id' expected a number but got <django.db.models.fields.related_descriptors.ForwardOneToOneDescriptor object at 0x000001A5ACE80250>."
'ForwardOneToOneDescriptor' object has no attribute 'id' is returned if I replace the filter argument with author=Profile.user.id
I'm not sure whether it's a problem with the way I filtered Posts, or how I used get_context_data.
The object is stored as self.object, so you can filter with:
class UserProfileView(DetailView):
model = Profile
context_object_name = 'profile'
template_name = 'users/profile.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user_posts'] = Post.objects.filter(author_id=self.object.user_id)
return context
An alternative might be to use a ListView for the Posts instead, to make use of Django's pagination:
from django.shortcuts import get_object_or_404
from django.views.generic import ListView
class UserProfileView(ListView):
model = Post
context_object_name = 'posts'
template_name = 'users/profile.html'
paginate_by = 10
def get_queryset(self, *args, **kwargs):
return (
super()
.get_queryset(*args, **kwargs)
.filter(author__profile__slug=self.kwargs['slug'])
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['profile'] = get_object_or_404(Profile, slug=self.kwargs['slug'])
return context
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
I have this code which for my views
class QuestionViewSet(viewsets.ModelViewSet):
queryset=Question.objects.all()
lookup_field="slug"
serializer_class=QuestionSerializer
permission_classes=[IsAuthorOrReadOnly,IsAuthenticated]
def perform_create(self, serializer):
print("user is", self.request.user)
serializer.save(author=self.request.user)
and this code for serializers
class QuestionSerializer(serializers.ModelSerializer):
author = serializers.StringRelatedField(read_only=True)
created_at = serializers.SerializerMethodField()
slug = serializers.SlugField(read_only=True)
answers_count = serializers.SerializerMethodField()
user_has_answered = serializers.SerializerMethodField()
print("author in serializer", author)
class Meta:
model = Question
exclude = ["updated_at"]
def get_created_at(self, instance):
return instance.created_at.strftime("%B %d, %Y")
def get_answers_count(self, instance):
return instance.answers.count()
def get_user_has_answered(self, instance):
request = self.context.get("request")
return instance.answers.filter(author=request.user).exists()
Then i have this code for models
class Question(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
content = models.CharField(max_length=240)
slug = models.SlugField(max_length=255, unique=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="questions")
def save(self, *args, **kwargs):
if not self.id:
# Newly created object, so set slug
self.slug = slugify(self.content)
super(Question, self).save(*args, **kwargs)
What i basically fail to do is to log the request .In case of function based i could easily log the request and see as what data is coming .However in case of class based views i fail to do this .
Also , I don't understand as where data is being saved in case of post request .I don't see ny save etc .
Please enlighten as how this works fine ?
To log a request data you can override a dispatch method:
class QuestionViewSet(viewsets.ModelViewSet):
def dispatch(self, request, *args, **kwargs)
# log the request here
return super().dispatch(request, *args, **kwargs)
Also you may consider using something more "broad" like setting your own midlleware.
As for create - magic happens in viewset create method. You can override it as well:
def create(self, request, *args, **kwargs):
# do something unusual here
instead of overriding the dispatch, better solution you can write the middleware which log the request data.
https://docs.djangoproject.com/en/3.0/topics/http/middleware/
lass SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
I'm writing a bit of Javascript for my app and I need to get a queryset to my .js file by serializing to JSON data first and then returning it to the HTML template. I followed the documentation and I thought I got it working the first time but I now realized that the data it returns aren't the same with what there is in the database.
Here's my views.py:
class DetailView(generic.DetailView):
model = Poll
template_name = 'voting/detail.html'
context_object_name = 'question'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
#note this line below
context['data'] = serializers.serialize("json", Profile.objects.filter(user_id=self.request.user))
return context
def get_queryset(self):
"""
Excludes any questions that aren't published yet.
"""
return super(DetailView, self).get_queryset().filter(
eligiblevoters__user=self.request.user,
pub_date__lte=timezone.now()
)
def get(self, request, *args, **kwargs):
try:
return super(DetailView, self).get(request, *args, **kwargs)
except Http404:
return render(request, 'voting/not_invited_to_poll.html', {})
Basically I'm putting into context a JSON serialized Queryset that contains data about the logged in user from the Profile model.
The profile model in models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
email_confirmed = models.BooleanField(default=False)
encrypted_private_key = models.BinaryField(max_length=500, blank=True)
public_key = models.BinaryField(max_length=30, blank=True)
salt = models.BinaryField(max_length=16, blank=True)
The output serialized data that is accessible in my HTML file is this
which looks correct at first but I noticed that even though the user id is correct the fields encrypted_private_key, public_key and salt are nothing like in the database.
The same queryset exported from my database for user id 14 is this:
I now it looks like a lot of data but just notice that the values for the fields encrypted_private_key, public_key and salt are completely different with what I get in JSON.
For a matter of fact if I remove the filtering of my table in my view like so:
class DetailView(generic.DetailView):
model = Poll
template_name = 'voting/detail.html'
context_object_name = 'question'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
#I change the line below to return the whole table
context['data'] = serializers.serialize("json", Profile.objects.all())
return context
def get_queryset(self):
"""
Excludes any questions that aren't published yet.
"""
return super(DetailView, self).get_queryset().filter(
eligiblevoters__user=self.request.user,
pub_date__lte=timezone.now()
)
def get(self, request, *args, **kwargs):
try:
return super(DetailView, self).get(request, *args, **kwargs)
except Http404:
return render(request, 'voting/not_invited_to_poll.html', {})
Now I get the whole table back serialized and what I notice is that those three fields have the same values for all users (in all the rows) even though that's not the case at all.
In the serialized data the encrypted_private_key, public_key and salt has the same value for all users, while in the database each user has a different value.
What am I doing wrong?
Ok, so it looks like Django's JSON serializer encodes BinaryField to base64 format by default. That's why the serialized data and the values from my database are completely different. I expected to see the same values but I guess that not the case which makes sense, I just didn't realize it soon enough...
In Django Admin I am displaying a url.
This url is created using the id the object that it is attached to.
I'm using python, django and django-rest-framework.
In my views I have logic on the ApiDetail class. Here I override the 'get' method.
I increment the current object in views.py:
currentObject = Api.objects.get(id=pk)
currentObject.currentNumber += 1
currentObject.save()
return self.retrieve(request, *args, **kwargs)
In models.py I set the url field:
class Api(models.Model):
myUrl = models.CharField(max_length=500, blank=True, verbose_name="Url", editable=False)
def save(self, *args, **kwargs):
self.formUrl = "https://custumUrl/"+str(self.id)+"/"
super(Api, self).save(*args, **kwargs)
Here I override the api save method to update the formUrl field.
The problem I have is when a form is first added to Django admin and saved the url says:
https://custumUrl/none/
It should say:
https://custumUrl/1/
Or any number, but definitely the number of the objects id.
I think Daniel is right in their comments and you should follow their advice.
But if you don't want to do that, then you should first save an object, then assign an id value to the url, then save it again:
class Api(models.Model):
myUrl = models.CharField(max_length=500, blank=True, verbose_name="Url", editable=False)
def save(self, *args, **kwargs):
super(Api, self).save(*args, **kwargs)
self.formUrl = "https://custumUrl/"+str(self.id)+"/"
super(Api, self).save(*args, **kwargs)
Is currentNumber defined in he Api class?
Also, in your Api class, you have myUrl defined, but in the save method it's formUrl.
Maybe try something like this:
class Api(models.Model):
formUrl = models.CharField(max_length=500, blank=True, verbose_name="Url", editable=False)
def save(self):
"""If this is the firsts time populate required details, otherwise update it."""
if not self.id:
latest_obj = Api.latest('id')
this_id = latest_obj.id
self.formUrl = "https://custumUrl/"+str(this_id)+"/"
super(Api, self).save()
else:
#Save it as is
super(Api, self).save()
I have a problem getting the user in django when I use django forms. My code looks something like this.
The view:
#login_required
def something(request):
item = ItemForm(request.POST)
item.save(user=request.user)
The form:
class ItemForm(ModelForm):
class Meta:
model = Item
fields = '__all__'
def save(self, *args, **kwargs):
user = kwargs['user']
super(ItemForm, self).save(user=user)
The model
class Item(models.Model):
field = models.CharField(max_length=100,)
field2 = models.CharField(max_length=100,)
def check_permissions(self, user):
return user.groups.filter(name='group').exists()
def save(self, *args, **kwargs):
if self.check_permissions(kwargs['user']):
super(Item, self).save()
My problem is that when I call the default save in ItemForm I get an error because the user param is unexpected. I need the user in the model to make the permission check but I dont know how to get it.
I finally solved the problem. The way I found was to save the form without the user but with the commit flag set to False and then calling the function save from the model with the user param.
The form save method now looks like this
def save(self, *args, **kwargs):
item = super(ItemForm, self).save(commit=False)
item.save(user=kwargs['user'])