Commit a065c242 authored by Florent Chehab's avatar Florent Chehab
Browse files

Merge branch 'post_data' into 'master'

Post data and color work

See merge request chehabfl/outgoing_rex!32
parents 05513735 c5398c92
Pipeline #26975 passed with stages
in 2 minutes and 49 seconds
...@@ -33,6 +33,10 @@ class MyModelSerializer(MySerializerWithJSON): ...@@ -33,6 +33,10 @@ class MyModelSerializer(MySerializerWithJSON):
pending_moderation = serializers.SerializerMethodField() pending_moderation = serializers.SerializerMethodField()
model_config = serializers.SerializerMethodField() model_config = serializers.SerializerMethodField()
# For easier handling on the client side, we force an id field
# this is usefull when a model has a dedicated primary key
id = serializers.SerializerMethodField()
def get_model_config(self, obj=None): def get_model_config(self, obj=None):
return self.Meta.model.model_config return self.Meta.model.model_config
...@@ -41,6 +45,9 @@ class MyModelSerializer(MySerializerWithJSON): ...@@ -41,6 +45,9 @@ class MyModelSerializer(MySerializerWithJSON):
return PendingModerationSerializer(obj.pending_moderation, many=True, read_only=True, context=self.context).data return PendingModerationSerializer(obj.pending_moderation, many=True, read_only=True, context=self.context).data
return None return None
def get_id(self, obj):
return obj.pk
class Meta: class Meta:
model = MyModel model = MyModel
......
...@@ -6,12 +6,14 @@ class DictModeViewSet(viewsets.ModelViewSet): ...@@ -6,12 +6,14 @@ class DictModeViewSet(viewsets.ModelViewSet):
ViewSet that renders data as dict with keys corresponding to the model ViewSet that renders data as dict with keys corresponding to the model
primary key. Instead of list. primary key. Instead of list.
""" """
BYPASS_DICT_MODE = False
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
response = super(viewsets.ModelViewSet, self).list( # pylint: disable=E1003 response = super(viewsets.ModelViewSet, self).list( # pylint: disable=E1003
request, *args, **kwargs) # call the original 'list' request, *args, **kwargs) # call the original 'list'
pk_attr_name = self.serializer_class.Meta.model._meta.pk.name if not self.BYPASS_DICT_MODE:
response.data = { pk_attr_name = self.serializer_class.Meta.model._meta.pk.name
d[pk_attr_name]: d for d in response.data response.data = {
} d[pk_attr_name]: d for d in response.data
}
return response return response
...@@ -23,11 +23,10 @@ class UserDataSerializer(MyModelSerializer): ...@@ -23,11 +23,10 @@ class UserDataSerializer(MyModelSerializer):
owner = serializers.CharField(read_only=True) owner = serializers.CharField(read_only=True)
def my_pre_save(self): def my_pre_save(self):
user = self.get_user_in_request() self.override_validated_data({'owner': self.user})
self.override_validated_data({'owner': user})
# we try to recover the correct instance # we try to recover the correct instance
query = UserData.objects.filter(owner=user) query = UserData.objects.filter(owner=self.user)
if len(query) == 1: if len(query) == 1:
self.instance = query[0] self.instance = query[0]
...@@ -39,6 +38,7 @@ class UserDataSerializer(MyModelSerializer): ...@@ -39,6 +38,7 @@ class UserDataSerializer(MyModelSerializer):
class UserDataViewSet(MyModelViewSet): class UserDataViewSet(MyModelViewSet):
permission_classes = get_viewset_permissions("UserDataViewSet") permission_classes = get_viewset_permissions("UserDataViewSet")
serializer_class = UserDataSerializer serializer_class = UserDataSerializer
BYPASS_DICT_MODE = True
def get_queryset(self): def get_queryset(self):
return UserData.objects.filter(owner=self.request.user) # pylint: disable=E1101 return UserData.objects.filter(owner=self.request.user) # pylint: disable=E1101
...@@ -10,19 +10,19 @@ ...@@ -10,19 +10,19 @@
// {{obj.NAME}} // {{obj.NAME}}
//////////////// ////////////////
export const {{obj.NAME}}_HAS_ERROR = '{{obj.NAME}}_HAS_ERROR'; export const {{obj.NAME}}_FETCH_HAS_ERROR = '{{obj.NAME}}_FETCH_HAS_ERROR';
export const {{obj.NAME}}_IS_LOADING = '{{obj.NAME}}_IS_LOADING'; export const {{obj.NAME}}_IS_LOADING = '{{obj.NAME}}_IS_LOADING';
export const {{obj.NAME}}_FETCH_DATA_SUCCESS = '{{obj.NAME}}_FETCH_DATA_SUCCESS'; export const {{obj.NAME}}_FETCH_DATA_SUCCESS = '{{obj.NAME}}_FETCH_DATA_SUCCESS';
export const {{obj.NAME}}_INVALIDATED = '{{obj.NAME}}_INVALIDATED'; export const {{obj.NAME}}_INVALIDATED = '{{obj.NAME}}_INVALIDATED';
export const {{obj.NAME}}_EL_HAS_ERROR = '{{obj.NAME}}_HAS_ERROR'; export const {{obj.NAME}}_EL_FETCH_HAS_ERROR = '{{obj.NAME}}_EL_FETCH_HAS_ERROR';
export const {{obj.NAME}}_EL_INVALIDATED = '{{obj.NAME}}_EL_INVALIDATED'; export const {{obj.NAME}}_EL_INVALIDATED = '{{obj.NAME}}_EL_INVALIDATED';
export const {{obj.NAME}}_EL_IS_LOADING = '{{obj.NAME}}_IS_LOADING'; export const {{obj.NAME}}_EL_IS_LOADING = '{{obj.NAME}}_EL_IS_LOADING';
export const {{obj.NAME}}_EL_FETCH_DATA_SUCCESS = '{{obj.NAME}}_FETCH_DATA_SUCCESS'; export const {{obj.NAME}}_EL_FETCH_DATA_SUCCESS = '{{obj.NAME}}_EL_FETCH_DATA_SUCCESS';
{% if not obj.read_only %} {% if not obj.read_only %}
export const {{obj.NAME}}_EL_IS_SAVING = '{{obj.NAME}}_IS_POSTING'; export const {{obj.NAME}}_EL_IS_SAVING = '{{obj.NAME}}_EL_IS_SAVING';
export const {{obj.NAME}}_EL_SAVING_DATA_SUCCESS = '{{obj.NAME}}_POST_DATA_SUCCESS'; export const {{obj.NAME}}_EL_SAVING_HAS_ERROR = '{{obj.NAME}}_EL_SAVING_HAS_ERROR';
{% endif %} {% endif %}
{% endfor %} {% endfor %}
...@@ -8,18 +8,19 @@ import Cookies from 'js-cookie'; ...@@ -8,18 +8,19 @@ import Cookies from 'js-cookie';
import { import {
{% for obj in data %} {% for obj in data %}
{{obj.NAME}}_HAS_ERROR, {{obj.NAME}}_FETCH_HAS_ERROR,
{{obj.NAME}}_IS_LOADING, {{obj.NAME}}_IS_LOADING,
{{obj.NAME}}_FETCH_DATA_SUCCESS, {{obj.NAME}}_FETCH_DATA_SUCCESS,
{{obj.NAME}}_INVALIDATED, {{obj.NAME}}_INVALIDATED,
{{obj.NAME}}_EL_INVALIDATED, {{obj.NAME}}_EL_INVALIDATED,
{{obj.NAME}}_EL_HAS_ERROR, {{obj.NAME}}_EL_FETCH_HAS_ERROR,
{{obj.NAME}}_EL_IS_LOADING, {{obj.NAME}}_EL_IS_LOADING,
{{obj.NAME}}_EL_FETCH_DATA_SUCCESS, {{obj.NAME}}_EL_FETCH_DATA_SUCCESS,
{% if not obj.read_only %} {% if not obj.read_only %}
{{obj.NAME}}_EL_IS_SAVING, {{obj.NAME}}_EL_IS_SAVING,
{{obj.NAME}}_EL_SAVING_HAS_ERROR,
{{obj.NAME}}_EL_SAVING_DATA_SUCCESS, {{obj.NAME}}_EL_SAVING_DATA_SUCCESS,
{% endif %} {% endif %}
...@@ -30,9 +31,12 @@ import { ...@@ -30,9 +31,12 @@ import {
// generic function definitions // generic function definitions
function _FetchData(pk, api_end_point, _IsLoading, _FetchDataSuccess, _Invalidated, _HasError, pk_required=false) { function _FetchData(pk, api_end_point, _IsLoading, _FetchDataSuccess, _Invalidated, _HasError, pk_required=false) {
if (pk_required && pk == ""){ if (pk_required && (typeof pk == 'undefined')){
throw "pk shouldn't be empty when requesting a specific element"; throw "pk shouldn't be empty when requesting a specific element";
} }
if (pk != ""){
api_end_point += pk;
}
return (dispatch) => { return (dispatch) => {
dispatch(_IsLoading(true)); dispatch(_IsLoading(true));
let token = Cookies.get('csrftoken'); let token = Cookies.get('csrftoken');
...@@ -49,19 +53,22 @@ function _FetchData(pk, api_end_point, _IsLoading, _FetchDataSuccess, _Invalidat ...@@ -49,19 +53,22 @@ function _FetchData(pk, api_end_point, _IsLoading, _FetchDataSuccess, _Invalidat
dispatch(_FetchDataSuccess(obj)); dispatch(_FetchDataSuccess(obj));
dispatch(_IsLoading(false)); dispatch(_IsLoading(false));
}) })
.catch(() => dispatch(_HasError(true))); .catch((e) => {
dispatch(_HasError(true, e));
dispatch(_IsLoading(false));
});
}; };
} }
function _ElSaveData(data, api_end_point, _IsLoading, _FetchDataSuccess, _Invalidated, _HasError) { function _ElSaveData(data, api_end_point, _ElIsSaving, _ElFetchDataSuccess, _ElInvalidated, _ElHasError) {
return (dispatch) => { return (dispatch) => {
let method = "POST"; let method = "POST";
let pk = ""; let pk = "";
if ('id' in data){ if ('id' in data){
method = "PUT"; method = "PUT";
pk = data.id; pk = data.id + '/';
} }
dispatch(_ElIsSaving(true)); dispatch(_ElIsSaving(true));
...@@ -86,20 +93,24 @@ function _ElSaveData(data, api_end_point, _IsLoading, _FetchDataSuccess, _Invali ...@@ -86,20 +93,24 @@ function _ElSaveData(data, api_end_point, _IsLoading, _FetchDataSuccess, _Invali
.then((response) => response.json()) .then((response) => response.json())
.then((_El) => { .then((_El) => {
dispatch(_ElInvalidated(false)); dispatch(_ElInvalidated(false));
dispatch(_ElSaveDataSuccess(_El)); dispatch(_ElFetchDataSuccess(_El)); // we use the same here
dispatch(_ElIsSaving(false)); dispatch(_ElIsSaving(false));
}) })
.catch(() => dispatch(_ElHasError(true))); .catch((e) => {
dispatch(_ElHasError(true, e))
dispatch(_ElIsSaving(false));
});
}; };
} }
///////////////////////////////// /////////////////////////////////
{% for obj in data %} {% for obj in data %}
export function {{obj.name}}HasError(bool) { export function {{obj.name}}FetchHasError(bool, error=null) {
return { return {
type: {{obj.NAME}}_HAS_ERROR, type: {{obj.NAME}}_FETCH_HAS_ERROR,
hasError: bool hasError: bool,
error
}; };
} }
...@@ -135,15 +146,16 @@ export function {{obj.name}}FetchData() { ...@@ -135,15 +146,16 @@ export function {{obj.name}}FetchData() {
{{obj.name}}IsLoading, {{obj.name}}IsLoading,
{{obj.name}}FetchDataSuccess, {{obj.name}}FetchDataSuccess,
{{obj.name}}Invalidated, {{obj.name}}Invalidated,
{{obj.name}}HasError {{obj.name}}FetchHasError
) )
} }
export function {{obj.name}}ElHasError(bool) { export function {{obj.name}}ElFetchHasError(bool, error) {
return { return {
type: {{obj.NAME}}_EL_HAS_ERROR, type: {{obj.NAME}}_EL_FETCH_HAS_ERROR,
hasError: bool hasError: bool,
error
}; };
} }
...@@ -180,7 +192,7 @@ export function {{obj.name}}ElFetchData(pk) { ...@@ -180,7 +192,7 @@ export function {{obj.name}}ElFetchData(pk) {
{{obj.name}}ElIsLoading, {{obj.name}}ElIsLoading,
{{obj.name}}ElFetchDataSuccess, {{obj.name}}ElFetchDataSuccess,
{{obj.name}}ElInvalidated, {{obj.name}}ElInvalidated,
{{obj.name}}ElHasError, {{obj.name}}ElFetchHasError,
true true
) )
} }
...@@ -195,19 +207,17 @@ export function {{obj.name}}ElIsSaving(bool) { ...@@ -195,19 +207,17 @@ export function {{obj.name}}ElIsSaving(bool) {
}; };
} }
export function {{obj.name}}ElSavingHasError(bool, error=null) {
export function {{obj.name}}ElSaveDataSuccess({{obj.name}}El) {
{{obj.name}}ElInvalidated(false)
return { return {
type: {{obj.NAME}}_EL_SAVING_DATA_SUCCESS, type: {{obj.NAME}}_EL_SAVING_HAS_ERROR,
{{obj.name}}El, hasError: bool,
{{obj.name}}ElSavedAt: Date.now() error
}; };
} }
export function {{obj.name}}ElSaveData(data) { export function {{obj.name}}ElSaveData(data) {
return _ElSaveData(data, "{{obj.api_end_point}}", {{obj.name}}ElIsLoading, {{obj.name}}ElFetchDataSuccess, {{obj.name}}ElInvalidated, {{obj.name}}ElHasError) return _ElSaveData(data, "{{obj.api_end_point}}", {{obj.name}}ElIsSaving, {{obj.name}}ElFetchDataSuccess, {{obj.name}}ElInvalidated, {{obj.name}}ElSavingHasError)
} }
{% endif %} {% endif %}
......
...@@ -9,18 +9,18 @@ import { combineReducers } from 'redux'; ...@@ -9,18 +9,18 @@ import { combineReducers } from 'redux';
import { import {
{% for obj in data %} {% for obj in data %}
{{obj.name}}Invalidated, {{obj.name}}Invalidated,
{{obj.name}}HasError, {{obj.name}}FetchHasError,
{{obj.name}}IsLoading, {{obj.name}}IsLoading,
{{obj.name}}Fetched, {{obj.name}}Fetched,
{{obj.name}}ElInvalidated, {{obj.name}}ElInvalidated,
{{obj.name}}ElHasError, {{obj.name}}ElFetchHasError,
{{obj.name}}ElIsLoading, {{obj.name}}ElIsLoading,
{{obj.name}}ElFetched, {{obj.name}}ElFetched,
{% if not obj.read_only %} {% if not obj.read_only %}
{{obj.name}}ElIsSaving, {{obj.name}}ElIsSaving,
{{obj.name}}ElSaved, {{obj.name}}ElSavingHasError,
{% endif %} {% endif %}
{% endfor %} {% endfor %}
...@@ -29,25 +29,21 @@ import { ...@@ -29,25 +29,21 @@ import {
{% for obj in data %} {% for obj in data %}
export const {{obj.name}}Reducers = combineReducers({ export const {{obj.name}}Reducers = combineReducers({
invalidated: {{obj.name}}Invalidated, invalidated: {{obj.name}}Invalidated,
hasError: {{obj.name}}HasError, fetchHasError: {{obj.name}}FetchHasError,
isLoading: {{obj.name}}IsLoading, isLoading: {{obj.name}}IsLoading,
fetched: {{obj.name}}Fetched, fetched: {{obj.name}}Fetched,
}) })
export const {{obj.name}}ElReducers = combineReducers({ export const {{obj.name}}ElReducers = combineReducers({
invalidated: {{obj.name}}ElInvalidated, invalidated: {{obj.name}}ElInvalidated,
hasError: {{obj.name}}ElHasError, fetchHasError: {{obj.name}}ElFetchHasError,
isLoading: {{obj.name}}ElIsLoading, isLoading: {{obj.name}}ElIsLoading,
fetched: {{obj.name}}ElFetched, fetched: {{obj.name}}ElFetched,
{% if not obj.read_only %} {% if not obj.read_only %}
isSaving: {{obj.name}}ElIsSaving, isSaving: {{obj.name}}ElIsSaving,
isSaved: {{obj.name}}ElSaved, savingHasError: {{obj.name}}ElSavingHasError
{% endif %} {% endif %}
}) })
{% endfor %} {% endfor %}
...@@ -7,17 +7,18 @@ ...@@ -7,17 +7,18 @@
import { import {
{% for obj in data %} {% for obj in data %}
{{obj.NAME}}_INVALIDATED, {{obj.NAME}}_INVALIDATED,
{{obj.NAME}}_HAS_ERROR, {{obj.NAME}}_FETCH_HAS_ERROR,
{{obj.NAME}}_IS_LOADING, {{obj.NAME}}_IS_LOADING,
{{obj.NAME}}_FETCH_DATA_SUCCESS, {{obj.NAME}}_FETCH_DATA_SUCCESS,
{{obj.NAME}}_EL_INVALIDATED, {{obj.NAME}}_EL_INVALIDATED,
{{obj.NAME}}_EL_HAS_ERROR, {{obj.NAME}}_EL_FETCH_HAS_ERROR,
{{obj.NAME}}_EL_IS_LOADING, {{obj.NAME}}_EL_IS_LOADING,
{{obj.NAME}}_EL_FETCH_DATA_SUCCESS, {{obj.NAME}}_EL_FETCH_DATA_SUCCESS,
{% if not obj.read_only %} {% if not obj.read_only %}
{{obj.NAME}}_EL_IS_SAVING, {{obj.NAME}}_EL_IS_SAVING,
{{obj.NAME}}_EL_SAVING_HAS_ERROR,
{{obj.NAME}}_EL_SAVING_DATA_SUCCESS, {{obj.NAME}}_EL_SAVING_DATA_SUCCESS,
{% endif %} {% endif %}
...@@ -25,10 +26,13 @@ import { ...@@ -25,10 +26,13 @@ import {
} from "./action-types"; } from "./action-types";
{% for obj in data %} {% for obj in data %}
export function {{obj.name}}HasError(state = false, action) { export function {{obj.name}}FetchHasError(state = {status: false, error: null}, action) {
switch (action.type) { switch (action.type) {
case {{obj.NAME}}_HAS_ERROR: case {{obj.NAME}}_FETCH_HAS_ERROR:
return action.hasError; return {
status: action.hasError,
error: action.error
}
default: default:
return state; return state;
...@@ -80,11 +84,13 @@ export function {{obj.name}}Fetched(state = { data: Object(), fetchedAt: null }, ...@@ -80,11 +84,13 @@ export function {{obj.name}}Fetched(state = { data: Object(), fetchedAt: null },
export function {{obj.name}}ElHasError(state = false, action) { export function {{obj.name}}ElFetchHasError(state = {status: false, error: null}, action) {
switch (action.type) { switch (action.type) {
case {{obj.NAME}}_EL_HAS_ERROR: case {{obj.NAME}}_EL_FETCH_HAS_ERROR:
return action.hasError; return {
status: action.hasError,
error: action.error
}
default: default:
return state; return state;
} }
...@@ -121,25 +127,27 @@ export function {{obj.name}}ElFetched(state = { data: Object(), fetchedAt: null ...@@ -121,25 +127,27 @@ export function {{obj.name}}ElFetched(state = { data: Object(), fetchedAt: null
export function {{obj.name}}ElIsSaving(state = false, action) { export function {{obj.name}}ElIsSaving(state = false, action) {
switch (action.type) { switch (action.type) {
case {{obj.NAME}}_EL_IS_SAVING: case {{obj.NAME}}_EL_IS_SAVING:
return action.isPosting; return action.isSaving;
default: default:
return state; return state;
} }
} }
export function {{obj.name}}ElSaved(state = { data: Object(), savedAt: null }, action) {
export function {{obj.name}}ElSavingHasError(state = {status: false, error: null}, action) {
switch (action.type) { switch (action.type) {
case {{obj.NAME}}_EL_SAVING_DATA_SUCCESS: case {{obj.NAME}}_EL_SAVING_HAS_ERROR:
return { return {
data: action.{{obj.name}}El, status: action.hasError,
fetchedAt: action.{{obj.name}}ElSavedAt error: action.error
} }
default: default:
return state; return state;
} }
} }
{% endif %} {% endif %}
{% endfor %} {% endfor %}
\ No newline at end of file
...@@ -108,6 +108,7 @@ class App extends MyComponent { ...@@ -108,6 +108,7 @@ class App extends MyComponent {
open: true, open: true,
}; };
handleDrawerOpen = () => { handleDrawerOpen = () => {
this.setState({ open: true }); this.setState({ open: true });
}; };
...@@ -178,7 +179,7 @@ App.propTypes = { ...@@ -178,7 +179,7 @@ App.propTypes = {
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { return {
countries: state.countries, countries: state.countries,
currencies: state.currencies currencies: state.currencies,
} }
}; };
...@@ -187,7 +188,7 @@ const mapDispatchToProps = (dispatch) => { ...@@ -187,7 +188,7 @@ const mapDispatchToProps = (dispatch) => {
fetchData: { fetchData: {
countries: () => dispatch(countriesFetchData()), countries: () => dispatch(countriesFetchData()),
currencies: () => dispatch(currenciesFetchData()), currencies: () => dispatch(currenciesFetchData()),
} },
}; };
}; };
......
...@@ -2,90 +2,169 @@ import React, { Component } from 'react'; ...@@ -2,90 +2,169 @@ import React, { Component } from 'react';
import Loading from './other/Loading'; import Loading from './other/Loading';
class MyComponent extends Component { class MyComponent extends Component {
customErrorHandlers = {}
getFetchedData(prop) { requireUserData = false;
return this.props[prop].fetched.data; // If true,
// You need to import
// userDataElFetchData,
// userDataElSaveData,
//your mapStateToProps should contain:
// const mapStateToProps = (state) => {
// return {
// userDataEl: state.userDataEl
// }
// };
//
// And your mapDispatchToProps should have:
// const mapDispatchToProps = (dispatch) => {
// return {
// fetchUserDataSingle: (id) => dispatch(userDataElFetchData(id)),
// fetchData: {
// userDataEl: () => dispatch(userDataElFetchData("")),
// },
// createUserData: () => dispatch(userDataElSaveData(Object()))
// };
// };
getFetchedData(prop) {
return this.props[prop].fetched.data;
}