I'm trying to return items from my sqlite db such that the url in the browser reflects the slug, rather than the unique post id.
I had this working before by using the primary key, whereby items were returned from the db when clicking a hyperlink for item detail - but swapping everything out for slug is not working.
I have a PostListView in react which lists my items from the db successfully, when I click on one of these the url field in the browser properly shows the slug. However, no data is returned from the db in the underlying detail view.
I am using django 2.2.1, django rest framework and react.
Back end - django and rest framework modules:
Models.py
class Post(models.Model):
post_id = models.UUIDField(
primary_key=True, default=uuid.uuid4, help_text='Unique ID for this post')
title = models.CharField(max_length=100)
content = models.TextField()
publish_date = models.DateTimeField(auto_now=True)
slug = models.SlugField(default='')
api/views.py
class PostListView(ListAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
class PostDetailView(RetrieveAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
lookup_field = 'slug'
api/urls.py
urlpatterns = [
path('', PostListView.as_view()),
path('<slug>', PostDetailView.as_view()),
]
api/serializers.py
class PostSerializer(serializers.ModelSerializer):
''' Serialisers will convert our JSON from web into python model '''
class Meta:
model = Post
fields = ('post_id', 'slug', 'title', 'content', 'publish_date' )
lookup_field = 'slug'
Frontend - React:
PostListView.js
class PostList extends React.Component {
state = {
posts: []
}
componentDidMount() {
// this function gets called everytime the component is mounted(rendered)
axios.get('http://127.0.0.1:8000/api/')
.then(res => {
this.setState({
posts: res.data
});
console.log(res.data);
})
}
render() {
return (
<Post data={this.state.posts} />
)
}
}
Post.js
const Post = (props) => {
const { classes, data } = props;
return (
<List className={classes.root}>
{data.map((item, key) =>
<ListItem alignItems="flex-start" key={item.post_id}>
<ListItemText
primary={<a href={`/${item.slug}`}>{item.title}</a>}
secondary={
<React.Fragment>
<Typography component="span" className={classes.inline} color="textPrimary">
{item.content}
</Typography>
</React.Fragment>
}
/>
</ListItem>
)}
</List>
);
}
PostDetailView.js
class PostDetail extends React.Component {
state = {
post: {}
}
componentDidMount() {
// this function gets called everytime the component is mounted(rendered)
const slug = this.props.match.params.slug;
axios.get(`http://127.0.0.1:8000/api/${slug}`)
.then(res => {
this.setState({
post: res.data
});
})
}
render() {
return (
<Card title={this.state.post.title}>
<p>{this.state.post.content}</p>
</Card>
)
}
}
I found that I needed to change the path handling the routing to PostDetail in my BaseRouter component as follows:
const BaseRouter = () => (
<div>
<Route exact path='/' component={PostList} />
<Route exact path='/:postID' component={PostDetail} />
</div>
);
To
<Route exact path='/:slug' component={PostDetail} />
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.