sorry if I ask very a simple question, I'm just starting to learn about WebDev and I have a question about django url and I used rest framework. I have http://localhost:8000/api/projectList/1 and I want to change id to the name in the url, for example http://localhost:8000/api/projectList/project
This is the code:
model.py
class ProjectList(models.Model):
project = models.CharField(max_length=200)
project_name = models.CharField(max_length=200)
category = models.CharField(max_length=200)
academic = models.CharField(max_length=200)
view.py
class ProjectListSerializer(serializers.ModelSerializer):
class Meta :
model = ProjectList
fields = '__all__'
class ProjectListViewset(viewsets.ModelViewSet):
queryset = ProjectList.objects.all()
serializers_class = ProjectListSerializer
class ProjectListListView(generics.ListAPIView):
serializer_class = ProjectListSerializer
def get_queryset(self):
return ProjectList.objects.all()
class ProjectId(generics.RetrieveUpdateDestroyAPIView):
queryset = ProjectList.objects.all()
serializer_class = ProjectListSerializer
urls.py
urlpatterns = [
path("", include(router.urls)),
path("projectList/", ProjectListListView.as_view()),
path("projectList/<int:pk>", ProjectId.as_view()),
]
Thank you, sorry for my simple question and my bad English.
Django Rest Framework ModelViewset doesn't require to declare separate view for detail. In ProjectListListView declare
lookup_field = 'project'
and delete
path("projectList/<int:pk>", ProjectId.as_view()),
from urlpatterns
In views and serializer define lookup_field = project, this will override the default id field.
Ps - make sure project is unique and primary key.
also change your url to accept string instead of int(id)
Related
I want my "CustomDetailView" to display a query(a single "flashcard"). I was able to it by using ListView
CustomListView(ListView):
model = Flashcard
template_name = 'flashcards.html
queryset = Flashcard.objects.all()[:1]
But for DetaiView I'm getting this error
Generic detail view CustomDetailView must be called with either an object pk or a slug in the URLconf.
class CustomDetailView(DetailView):
model = Flashcard
template_name = "flashcards.html"
queryset = Flashcard.objects.all().first()
urls.py
path('', CustomDetailView.as_view(), name='flashcards'),
How to fix this?
remove first from queryset:
class CustomDetailView(DetailView):
model = Flashcard
template_name = "flashcards.html"
queryset = Flashcard.objects.all()
lookup_field = 'pk'
lookup_url_kwarg = 'pk'
enter code here
and add id to url:
path('/<int:pk>/', CustomDetailView.as_view(), name='flashcards'),
You need your pk or slug in
path('', CustomDetailView.as_view(), name='flashcards'),
See an example from the docs:
urlpatterns = [
path('<slug:slug>/', ArticleDetailView.as_view(), name='article-detail'),
]
This is because DetailView is inheriting from SingleObjectMixin, and it fetches objects that way. If you want, jump to definition of this class in your IDE to see its implementation of get_queryset and get_object.
How you call your pk or slug really depends on your model, the Flashcard. If your primary_key field is an Integer, I think you'll be fine writing
path('<int:pk>', CustomDetailView.as_view(), name='flashcards'),
edit: as Pourya Mansouri wrote, you do need to remove queryset attribute in your case too
The slug you can customize with slug_field and similar attributes.
You can define a completely custom object fetching behavior by overriding get_object or get_queryset. Here's a random examples of doing that I found on the internet:
Overriding get_object: https://www.valentinog.com/blog/detail/
Overriding get_queryset: https://www.agiliq.com/blog/2019/01/django-when-and-how-use-detailview/
Maybe should have better communicated. Sorry for my English.
I wanted to return a random query when I clicked on a link a show it on detailView. I was able to it by this. Don't think it's efficient. If anyone has any idea share it.
def get_object(self):
queryset = Flashcard.objects.order_by('?').first()
return queryset
This question already has an answer here:
django use Pk field in a url
(1 answer)
Closed 4 years ago.
if anyone can help me with how to rename pk for a class(table) when I use it in the urlpattern.
(my goal is using many classes(tables ) PK in the same url and I want to know how to rename PK field for each table)
My code below is just for one class(table) for simplicity:
models.py:
class School(models.Model):
name = models.CharField(max_length=256)
principal = models.CharField(max_length=256)
location = models.CharField(max_length=256)
def __str__(self):
return str(self.name)
def get_absolute_url(self):
return reverse("basic_app:school_detail",kwargs={'pk':self.pk})
views.py:
class SchoolListView(ListView):
model = models.School
class SchoolDetailView(DetailView):
context_object_name = 'school_details'
model = models.School
template_name = 'basic_app/school_detail.html'
and my current urls.py (which I want to edit) :
urlpatterns = [
url(r'^$',views.SchoolListView.as_view(),name='school_list'),
url(r'^(?P<pk>\d+)/$',views.SchoolDetailView.as_view(),name='school_detail'),]
So my goal is to make the urls.py file look like this(school_pk instead of pk):
urlpatterns = [
url(r'^$',views.SchoolListView.as_view(),name='school_list'),
url(r'^(?P<school_pk>\d+)/$',views.SchoolDetailView.as_view(),name='school_detail'),]
I know I should add a function below class SchoolDetailView in views.py but I don’t know how ?
I am still learning so any help will be highly appreciated..Thanks in Advance
When declaring your model you can make a field primary by including primary_key=True, eg
class School(models.Model):
name = models.CharField(max_length=256, primary_key=True)
You can then use name within your URL.
I would recommend using unique slug fields.
EDIT
The URL isn't interested if it is called pk or slug or name. It is interested in the pattern. If you have a URL patterns
url(r'^(?P<slug>[\w\\-]+)/$', views.Type1View.as_view(), name="slug"),
url(r'^(?P<city>[\w\\-]+)/$', views.Type2View.as_view(), name="city"),
Both {% url 'slug' slug %} and {% url 'city' slug %} will produce a URL or /slug/ and both will call Type1View.as_view()
You need to ensure your URLs are unique.
If you want to refer to 3 tables then your url pattern is
url(r'^(?P<slug>[\w\\-]+)/(?P<slug1>[\w\\-]+)/(?P<slug2>[\w\\-]+)/$', MyView.as_view(), name="someName"),
And then within your view define def get_objects(self) or def get_queryset(self) to pick up the info you want.
I have two models (School and Student) with one to many relationship.
my models.py are set as follows:
class School(models.Model):
name = models.CharField(max_length=256)
principal = models.CharField(max_length=256)
location = models.CharField(max_length=256)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse("basic_app:school_detail",kwargs={'pk':self.pk})
class Student(models.Model):
name = models.CharField(max_length=256)
age = models.PositiveIntegerField()
school = models.ForeignKey(School,related_name='students',on_delete=models.CASCADE,default=None)
and the urls.py file will have the following:
urlpatterns = [
url(r'^$',views.SchoolListView.as_view(),name='school_list'),
url(r'^(?P<pk>\d+)/$',views.SchoolDetailView.as_view(),name='school_detail'),
url(r'^create/$',views.SchoolCreateView.as_view(),name='school_create'),
url(r'^update/(?P<pk>\d+)/$',views.SchoolUpdateView.as_view(),name='school_update'),
url(r'^delete/(?P<pk>\d+)/$',views.SchoolDeleteView.as_view(),name='school_delete'),
my question is I want to add to the urlpatterns list more urls for students:something like
#url(r'^$',views.StudentListView.as_view(),name='student_list'),
# url(r'^(?P<pk>\d+)/$',views.StudentDetailView.as_view(),name='student_detail'),
# url(r'^create/$',views.StudentCreateView.as_view(),name='student_create'),
# url(r'^update/(?P<pk>\d+)/$',views.StudentUpdateView.as_view(),name='student_update'),
# url(r'^delete/(?P<pk>\d+)/$',views.StudentDeleteView.as_view(),name='student_delete')
I know my addition is not correct because each group of students should be related to a specific school. I am confused how to use id/pk in the url because for example student_detail.html url should contain both school id and student id.
sorry if this looks naive question.. but I am still learning and any help will be highly appreciated..
thanks
You could add another argument school_pk to the URL pattern, for example:
url(r'^school/(?P<school_pk>\d+)/update/(?P<pk>\d+)/$',views.StudentUpdateView.as_view(),name='student_update'),
Then filter the queryset to only include students from that school.
class StudentUpdateView(UpdateView):
def get_queryset(self):
queryset = super(StudentUpdateView, self).get_queryset()
return queryset.filter(school=self.kwargs['school_pk'])
The UpdateView will take care of fetching the student, because you're still using pk for the student pk.
You need to add 'student(s)' to the urls to make them distinct. You may not need to add school_id to student urls as they are related by ForeignKey
url(r'^students$',views.StudentListView.as_view(),
name='student_list'),
url(r'^student/(?P<pk>\d+)/$',views.StudentDetailView.as_view(),
name='student_detail'),
url(r'^student/create/$',views.StudentCreateView.as_view(),
name='student_create'),
url(r'^student/update/(?P<pk>\d+)/$',views.StudentUpdateView.as_view(),
name='student_update'),
url(r'^student/delete/(?P<pk>\d+)/$',views.StudentDeleteView.as_view(),
name='student_delete')
I am trying to get a simple nested route set up with drf-extensions but am having trouble following the docs, I am getting this error:
TypeError at /data/
register() got an unexpected keyword argument 'parents_query_lookups'
Trying to achieve /data/Survey/<survey>/Version/<version>/Product/<product>/
each survey has multiple versions and those versions will contain multiple Products e.g., /data/survey/survey1_name/version/survey1_version1/product/survey1_version_product1/
but currently I have un-nested endpoints
/data/survey/
/data/versions/
/data/products/
models.py
class Survey(models.Model):
survey = models.CharField(choices=SURVEYS, max_length=100)
class Version(models.Model):
version = models.CharField(choices=DATA_RELEASES, max_length=50)
survey = models.ForeignKey(Survey)
class Product(models.Model):
product = models.CharField(choices=PRODUCTS, max_length=100)
version = models.ForeignKey(Version)
views.py
class SurveyViewSet(NestedViewSetMixin, viewsets.ModelViewSet):
queryset = Survey.objects.all()
serializer_class = SurveySerializer
permission_classes = [permissions.AllowAny]
class VersionViewSet(NestedViewSetMixin, viewsets.ModelViewSet):
queryset = Version.objects.all()
serializer_class = VersionSerializer
permission_classes = [permissions.AllowAny]
class ProductViewSet(NestedViewSetMixin, viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
permission_classes = [permissions.AllowAny]
serializers.py
class SurveySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Survey
fields = ('id', 'survey')
class VersionSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Version
fields = ('id', 'version', 'survey')
class ProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Product
fields = ('id', 'product', 'version')
urls.py
router = ExtendDefaultRouter()
router.register(r'surveys', views.SurveyViewSet, base_name='survey')
router.register(r'versions', views.VersionViewSet, parents_query_lookups=['survey']),
router.register(r'products', views.ProductViewSet, parents_query_lookups=['version'])
urlpatterns = [
url(r'^data/', include(router.urls)),
]
You should chain the calls to register(), instead of calling them directly on the router :
urls.py
router = ExtendDefaultRouter()
(router.register(r'surveys', views.SurveyViewSet, base_name='survey')
.register(r'versions', views.VersionViewSet, parents_query_lookups=['survey']),
.register(r'products', views.ProductViewSet, parents_query_lookups=['version']))
That's because NestedRouterMixin.register() returns an instance of NestedRegistryItem, which is the class that understand the parents_query_lookups parameter.
In order to keep your project well organized in accordance with the hierarchy of your data, the first thing I would do is separate each tier of urls into a separate entity, and possibly even a separate file. The second would be to add an primary-key endpoint to each tier of the rest API. In the end it would look something like this:
survey_urls.py
router.register(r'survey', views.SurveyViewSet, base_name='survey')
router.register(r'survey-version/', include('my_app.rest_server.version_urls'))
version_urls.py
router.register(r'version', views.VersionViewSet, base_name='version')
router.register(r'version-product/', include('my_app.rest_server.product_urls'))
product_urls.py
router.register(r'product/(?P<pk>[0-9]+)$', views.ProductViewSet, base_name='product')
Then, your final url could look like: /data/survey-version/33/version-product/5/product/8
In all likelihood, this is an unnecessary complication of things, since each product has its own unique id, and then you can just access it with a url like: /data/product/pk, using a single url routing, namely the last line of code above.
My examples assumed the records have a numeric primary key, if the case is otherwise you'd have to change the regex accordingly.
New to DRF and everything works as long as I don't include 'url' in fields. Here's what I've got:
Serializer:
class TaskSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Task
fields = ('pk', 'short_desc', 'scheduled_date')
View Set:
class TaskViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Task.objects.all().order_by('scheduled_date')
serializer_class = TaskSerializer
URLs:
router = routers.DefaultRouter()
router.register(r'tasks', views.TaskViewSet)
urlpatterns = [
[... bunch of non-REST URLs]
# REST API
url(r'^', include(router.urls)),
At runtime, printing router.urls gives me:
<RegexURLPattern api-root ^$>
<RegexURLPattern api-root ^\.(?P<format>[a-z0-9]+)/?$>
<RegexURLPattern task-list ^tasks/$>
<RegexURLPattern task-list ^tasks\.(?P<format>[a-z0-9]+)/?$>
<RegexURLPattern task-detail ^tasks/(?P<pk>[^/.]+)/$>
<RegexURLPattern task-detail ^tasks/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$>
Both [...]/tasks/ and [...]/tasks/123/ work if I type them into my browser, which leads me to believe that task-list and task-detail views do, in fact exist.
Now I introduce a problem by adding 'url' to the serializer:
class TaskSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Task
fields = ('url', 'pk', 'short_desc', 'scheduled_date')
After adding 'url' to fields, I get the following error:
Could not resolve URL for hyperlinked relationship using view name
"task-detail". You may have failed to include the related model in
your API, or incorrectly configured the lookup_field attribute on
this field.
The DRF docs say:
There needs to be a way of determining which views should be used for
hyperlinking to model instances. By default hyperlinks are expected to
correspond to a view name that matches the style '{model_name}-detail',
and looks up the instance by a pk keyword argument.
Since I've verified that task-detail exists and that the corresponding URL [...]/tasks/123/ works, I can't for the life of me figure out why DRF can't form the URL. Any ideas?
Inspired by clues revealed by Kunkka's answer, I have a solution that looks like this:
class TaskSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name="task:task-detail")
class Meta:
model = Task
fields = ('url', 'pk', 'short_desc', 'scheduled_date')
I.e. I've added an url = [...] line to the serializer I originally posted in my question. This solves the lookup problem which was presumably caused by DRF not knowing that 'task-detail' is actually in the 'task' namespace.
Any better solutions?
Can you try this?
class TaskSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.SerializerMethodField()
class Meta:
model = Task
fields = ('pk','url', 'short_desc', 'scheduled_date')
def get_url(self,obj):
request = self.context['request']
return = {'self':reverse('task-detail',kwargs={'pk':obj.pk},request=request)}