Codementor Events

Creating React Native apps with Django rest-api

Published Aug 22, 2017Last updated Feb 17, 2018
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.

Discover and read more posts from Hassan Abid
get started
post commentsBe the first to share your opinion
Raj Desai
4 years ago

Hi, so I have two silly questions.

  1. What role does node.js play( I am a django developer and haven’t gone into node
    so I wanted to know its part.)
  2. Can we build heavy apps like instagram,uber etc. on it and if not what platform should I use.
Miguel Cordova
7 years ago

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.

Hassan Abid
6 years ago

Hi @Maxus, I will try to add an example of it. Have you added csrf_exempt ? I will update you with it. Thanks

Rahul Sharma
4 years ago

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.

  function readCookie(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(";");
    for (var i = 0; i < ca.length; i++) {
      var c = ca[i];
      while (c.charAt(0) === " ") c = c.substring(1, c.length);
      if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
    }
    return null;
  }
  let csrfToken = readCookie("csrftoken");

Pass it in the header:

axios
        .post({url}, data, {
          headers: {
            "Content-Type": "application/json",
            "X-CSRFToken": csrfToken,
          },
        })
        .then((res) => {
          console.log(res);
        });
Laszlo Marai
7 years ago

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).

Hassan Abid
7 years ago

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!

Laszlo Marai
7 years ago

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 :).

Hassan Abid
7 years ago

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/

Show more replies