Edit3: the culprit is re_path, not foreign key
Original:
This bug is so subtle I couldn't really find a niche way to describe it, For example I have two apps, News and Blogs
In blogs.py model I have something like this:
class BlogType(models.Model):
blog_type = CharField(max_length=20)
def __str__(self):
return self.blog_type
class Blogs(models.Model):
title = models.CharField(max_length=20)
blog_author = models.ForeignKey(User, on_delete=models.CASCADE)
blog_type = models.ForeignKey(BlogType, on_delete=models.DO_NOTHING)
here a blog_type is a foreignkey defined inside the same models.py
In blogs url.py
from django.urls import path, re_path
from . import views
from .views import blogspage
urlpatterns = [
path('', views.blogs, name='blogs'),
re_path(r'(?P<blog_type>[\w-] )/(?P<pk>[0-9] )$', blogspage.as_view(), name='blogspage'),
]
Here using the forignkey as a url parameter
And in blogs views.py
class blogspage(DetailView):
model=Blogs
template_name='blogs/blogspage.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
print('Print the object if this is executed', self.get_object())
In Django template you would pass something like:
<div><a href="{% url 'blogspage' b.blog_type b.id %}">{{b.title}}</a></div>
Now the news model.py you have:
class NewsType(models.Model):
news_type = CharField(max_length=20)
def __str__(self):
return self.news_type
class News(models.Model):
title = models.CharField(max_length=20)
news_author = models.ForeignKey(User, on_delete=models.CASCADE)
news_type = models.ForeignKey(NewsType, on_delete=models.DO_NOTHING)
The news views, news urls, news templates, are the exactly the same as blog except the name and template name, basically replacing every single "blogs" to "news"
Then here is where the bug would occur, Only one detailed view will ever execute, for example when someone clicks "{%url "blogspage" blog.blog_type blog.id%}
" It will go to url blog/blog_type/pk but the content will be news, However, If <foreignkey> blog_type
is removed from url parameter, Only then the detailed view of blog will execute and the correct blog content will be rendered.
news url.py:
urlpatterns = [
path('', views.news, name='news'),
re_path(r'(?P<news_type>[\w-] )/(?P<pk>[0-9] )$', newspage.as_view(), name='newspage'),
]
root url.py
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('news.urls')),
path('blogs/',include('blogs.urls')),
] # path('news/', include('news.urls')), will not conflit
# switch the position between blogs.urls and news.urls also no conflit
Edit: www.example.com/blogs/blog_type/1 and www.example.com/news/news_type/1 will not conflit but www.example.com/blogs/blog_type/1 and www.example.com/news_type/1 will
Edit2: Because "" and "/blogs" bugged I automatically assume "/blogs" and "/news" will also bug, But the bug only exist between "" and "/blogs"
CodePudding user response:
It looks like an issue with Django path
and re_path
method, where as per your scenario the re_path
for both blogs and news URLs for newspage
and blogpage
are matching and django is not able to resolve the correct URL and redirect on the first matching URL.