Creating React Native apps with Django rest-api
Code Repo : https://github.com/hassanabidpk/react_pyconlunch Please show support by pressing star button.
Last week, I delivered a talk about Django for mobile applications at Pycon Korea. Over the past 6 years, I have been mostly developing mobile applications and contributing to company’s SDKs. Things have changed over past couple of years, as I am no more depending on backend developers to spin off a server for me. Neither I am interested to use automated services like Parse (RIP) or Firebase which hides the complexity and elegance of a backend for mobile applications. I decided to use Django as backend for my mobile applications. Its flexible, stable and customizable. In this blog post, I am going to share basic steps for developing a React Native app with Django rest-api.
So Why React Native? I have been mostly developing native mobile apps But sometimes it seems like time consuming to develop a simple app with same functionalities for both iOS and Android. Not that long ago, I started learning React Native and got more interested in it.
I will use an example of Restaurant (Pycon Lunch) app for this blog post. You can find the source code on Github. In the branch azure, You can find rest api code and other necessary files for deploying on Azure
First of all, I made a simple model for a restaurant object.
from django.db import models
from django.contrib.auth.models import User
class Restaurant(models.Model):
name = models.CharField(max_length=200)
address = models.TextField()
photo = models.ImageField(upload_to="food/photos/", null=True, blank=True)
menu = models.TextField()
tags = models.CharField(max_length=200)
pub_date = models.DateTimeField(auto_now_add=True)
writer = models.ForeignKey(User)
def __str__(self):
return self.name
Second step is to create a serializer class using Django Rest framework.
from rest_framework import serializers
from food.models import Restaurant
class RestaurantSerializer(serializers.ModelSerializer):
class Meta:
model = Restaurant
fields = ('id', 'name', 'address', 'photo', 'tags', 'menu', 'pub_date')
Finally, we create an api function in views.py which will server requests at /api/list end poin_t_
from django.shortcuts import render
from django.http import JsonResponse
from .models import Restaurant
from .serializers import RestaurantSerializer
from django.views.decorators.csrf import csrf_exempt
def index(request):
rest_list = Restaurant.objects.order_by('-pub_date')
context = {'rest_list': rest_list}
return render(request, 'food/index.html', context)
# Rest api end point
def get_rest_list(request):
"""
Returns Json list of all restaurants
"""
if request.method == "GET":
rest_list = Restaurant.objects.order_by('-pub_date')
serializer = RestaurantSerializer(rest_list, many=True)
return JsonResponse(serializer.data, safe=False)
We are ready for creating our React Native app now. The JSON response is like this
[{
"id": 3,
"name": "Newyork Pizza",
"address": "Coex mall west gate, first floor, Seoul",
"photo": "/media/food/photos/piza.jpg",
"tags": "pizza",
"menu": "1. Cheese pizza\r\n2. Vegetable Pizza\r\n3. Chicken Pizza",
"pub_date": "2017-08-05T16:34:08.094007Z"
}]
For creating React Native app, we use CRNA (Create React Native App). Its an easy way to start building new applications. It works with expo sdk and apps so you can test your apps conveniently on mobile devices as well as simulator. Read the full guide about getting started here. The boilerplate code will give you an App.js main file and some other helper files like package.json and App.test.js.
Let’s start adding some code to App.js (Our Starting point of the app). We use react-navigation package for handling the navigation in the app. Create a StackNavigator which sets HomeScreen as landing page and DetailScreen as Detail page.
// @flow
import React from 'react';
import { StyleSheet, Text, FlatList, ActivityIndicator, View, Image } from 'react-native';
import { List, ListItem, SearchBar, Avatar } from "react-native-elements";
import { StackNavigator } from 'react-navigation';
import { constants } from 'expo';
import HomeScreen from './src/components/home';
import DetailScreen from './src/components/detail';
export default StackNavigator({
Home: { screen: HomeScreen,
navigationOptions: {
title: 'Home',
headerBackTitle: 'Back',
},
},
Detail: { screen: DetailScreen,
navigationOptions: {
title: 'Detail',
},
}
});
In HomeScreen component, we have to fetch data from server and render it to the view. Our api end point for this example is http://pyconlunchbeta.azurewebsites.net/api/list
import React from 'react';
import { StyleSheet, Text, FlatList, ActivityIndicator, View, Image } from 'react-native';
import { List, ListItem, SearchBar, Avatar } from "react-native-elements";
import { StackNavigator } from 'react-navigation';
export default class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
error: null,
refreshing: false,
base_url: "https://pyconlunchbeta.azurewebsites.net"
}
}
componentDidMount() {
this.fetchDataFromApi();
}
fetchDataFromApi = () => {
const url = "https://pyconlunchbeta.azurewebsites.net/api/list.json";
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res,
error: null,
loading: false,
refreshing: false
});
})
.catch(error => {
this.setState({ error, loading : false });
})
};
handleRefresh = () => {
this.setState(
{
refreshing: true
},
() => {
this.fetchDataFromApi();
}
);
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%",
marginTop: "3%"
}}
/>
);
};
renderHeader = () => {
return <SearchBar placeholder="Type Here..." lightTheme round />;
};
render() {
return (
<List containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
onPress={() => this.props.navigation.navigate('Detail',
{name: `${item.name}`, menu: `${item.menu}`,
img: `${this.state.base_url}${item.photo}`,
address: `${item.address}`})}
avatar={<Avatar
source={{uri: `${this.state.base_url}${item.photo}`}}
onPress={() => console.log("Works!")}
containerStyle={{marginBottom: 2}}
avatarStyle={{resizeMode: "cover"}}
width={140}
height={130}
/>}
title={`${item.name}`}
titleStyle={{ fontSize: 16}}
titleContainerStyle = {{ marginLeft: 120 }}
subtitle={<View style={styles.subtitleView}>
<Text style={styles.menuText}>{item.menu}</Text>
<Text style={styles.locText}>{item.address}</Text>
</View>}
containerStyle={{ borderBottomWidth: 0, marginBottom: 20 }}
/>
)}
keyExtractor={item => item.id}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
onRefresh={this.handleRefresh}
refreshing={this.state.refreshing}
/>
</List>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
subtitleView: {
flexDirection: 'column',
paddingLeft: 10,
paddingTop: 5,
marginLeft: 110
},
menuText: {
paddingLeft: 10,
color: 'grey'
},
locText: {
paddingLeft: 10,
color: 'grey',
marginTop: 6,
fontSize: 12
},
titleText: {
fontWeight: 'bold'
},
restaurantImage: {
width: 600,
height: 800
}
});
In the fetchDataFromApi function, we user fetch api to pull in data and start rendering from their on. For convenience and fast prototyping, I used react-native elements package which provides useful UI components such as Avatar, List View and Search bar.
You can see the DetailScreen’s finished code here. It doesn’t do any network call, because we are passing on data parameters from home screen to detail screen using onPress callback and navigation props.
After you are done with code, you can test your app on Android or iOS devices. When you execute npm start. There is QR code that appears in the terminal which you can scan with Expo app (You don’t need to install Xcode or Android Studio in this case). Your device and computer should be on same network. If you want to test app on iPhone simulator. execute npm run ios in terminal. Make sure that you have installed Xcode on your Macbook. You can try out expo APIs here
In Short, React Native is powerful framework for building iOS and Android apps. It is ideal for small projects and fast prototyping. Find the full source code of React Native app (PyconLunch) on Github. The next part of this blog post will be about authentication and registration of users.
Hi, so I have two silly questions.
so I wanted to know its part.)
Hi, we are trying to develop a React Native app with DRF, we can consult data without problem, but when React try to send the parameters by POST, the data never arrives to DRF. I would love to see the correct way to send data, and if is possible also files.
Hi @Maxus, I will try to add an example of it. Have you added csrf_exempt ? I will update you with it. Thanks
You’ve got two ways, in my opinion, either use csrf_exempt in ‘urls.py’ (NOT RECOMMENDED according to my knowledge) or you could fetch the cookies in a variable and then send it with the header in the json POST request.
Pass it in the header:
You should definitely use DRF’s Views and Viewsets instead of creating view functions (which I wouldn’t use in plain Django either, but that’s just a personal choice).
Your rest endpoint can be written without a single line of imperative code (in the style of the Model and Serializer definition) using ListAPIView (http://www.django-rest-framework.org/api-guide/generic-views/#listapiview).
Thanks @Laszlo for your feedback. I wrote this sample code for beginners to Django. I have another advance version with using ViewSets and ListAPIView. But I will try to make an advance version of this tutorial too!
You’re welcome! I’d argue that for beginners I’d start with the correct solution showing the higher level concepts and then I would explain how it works (which is kind of like what your function based view imitates). Partly because it’s hard to unlearn something that you have learned (a beginner may look at your post and then start grinding away on their code, not looking back for quite a while), partly because this is the way to do the simple cases, and you only need to write your own views in special cases. So as a beginner, you don’t even need to know about it.
But that’s just my opinion, my way of teaching :).
Pretty much agree with you. I will change the tutorial based on your suggestions! My audience was afraid to go beyond this page But i guess they should learn class based views :) http://www.django-rest-framework.org/tutorial/1-serialization/