На ваш вопрос уже был дан ответ здесь , но я хотел бы рассказать вам о моем обходном пути для работы React-Admin с отношениями «многие ко многим».
Как сказано в упомянутом ответе, вы должны расширить DataProvider, чтобы он мог извлекать ресурсы отношения «многие ко многим». Однако вам нужно использовать новый глагол REST, давайте предположим, что GET_MANY_MANY_REFERENCE
где-то в вашем приложении. Поскольку разные службы / API REST могут иметь разные форматы маршрутов для извлечения связанных ресурсов, я не пытался создать новый DataProvider, я знаю, что это не лучшее решение, но для коротких сроков это довольно просто.
Мое решение вдохновляло
и создавало новый компонент
для отношений «многие ко многим». Этот компонент извлекает связанные записи в componentDidMount
, используя fetch API . При ответе использует данные ответа для построения на объектах, один из которых представляет собой объект с ключами, являющимися идентификаторами записей, и оценивает соответствующий объект записи, а также массив идентификаторов с идентификаторами записей. Это передается дочерним элементам вместе с другими переменными состояния, такими как page, sort, perPage, total, для обработки разбиения на страницы и упорядочения данных. Помните, что изменение порядка данных в Datagrid означает новый запрос к API. Этот компонент разделен на контроллер и представление, например
, где контроллер извлекает данные, управляет ими и передает их дочерним элементам, а представление, которое получает данные контроллера и передает их дочерним элементам, отображает его содержимое. Это позволило мне отобразить данные отношений «многие ко многим» в Datagrid, даже если с некоторыми ограничениями это компонент для агрегирования в мой проект, и он работает только с моим текущим API, если что-то изменится, и я должен изменить поле на: но на данный момент он работает и может быть использован в моем приложении.
Детали реализации выглядят следующим образом:
//ReferenceManyManyField
export const ReferenceManyManyField = ({children, ...prop}) => {
if(React.Children.count(children) !== 1) {
throw new Error( ' only accepts a single child (like )' )
}
return
{controllerProps => ( )}
//ReferenceManyManyFieldController
class ReferenceManyManyFieldController extends Component {
constructor(props){
super(props)
//State to manage sorting and pagination, uses some props from react-redux
//I discarded react-redux for simplicity/control however in the final solution react-redux might be incorporated
this.state = {
sort: props.sort,
page: 1,
perPage: props.perPage,
total: 0
}
}
componentWillMount() {
this.fetchRelated()
}
//This could be a call to your custom dataProvider with a new REST verb
fetchRelated({ record, resource, reference, showNotification, fetchStart, fetchEnd } = this.props){
//fetchStart and fetchEnd are methods that signal an operation is being made and make active/deactivate loading indicator, dataProvider or sagas should do this
fetchStart()
dataProvider(GET_LIST,`${resource}/${record.id}/${reference}`,{
sort: this.state.sort,
pagination: {
page: this.state.page,
perPage: this.state.perPage
}
})
.then(response => {
const ids = []
const data = response.data.reduce((acc, record) => {
ids.push(record.id)
return {...acc, [record.id]: record}
}, {})
this.setState({data, ids, total:response.total})
})
.catch(e => {
console.error(e)
showNotification('ra.notification.http_error')
})
.finally(fetchEnd)
}
//Set methods are here to manage pagination and ordering,
//again uses react-redux to manage this
setSort = field => {
const order =
this.state.sort.field === field &&
this.state.sort.order === 'ASC'
? 'DESC'
: 'ASC';
this.setState({ sort: { field, order } }, this.fetchRelated);
};
setPage = page => this.setState({ page }, this.fetchRelated);
setPerPage = perPage => this.setState({ perPage }, this.fetchRelated);
render(){
const { resource, reference, children, basePath } = this.props
const { page, perPage, total } = this.state;
//Changed basePath to be reference name so in children can nest other resources, not sure why the use of replace, maybe to maintain plurals, don't remember
const referenceBasePath = basePath.replace(resource, reference);
return children({
currentSort: this.state.sort,
data: this.state.data,
ids: this.state.ids,
isLoading: typeof this.state.ids === 'undefined',
page,
perPage,
referenceBasePath,
setPage: this.setPage,
setPerPage: this.setPerPage,
setSort: this.setSort,
total
})
}
}
ReferenceManyManyFieldController.defaultProps = {
perPage: 25,
sort: {field: 'id', order: 'DESC'}
}
//ReferenceManyManyFieldView
export const ReferenceManyManyFieldView = ({
children,
classes = {},
className,
currentSort,
data,
ids,
isLoading,
page,
pagination,
perPage,
reference,
referenceBasePath,
setPerPage,
setPage,
setSort,
total
}) => (
isLoading ?
:
{React.cloneElement(children, {
className,
resource: reference,
ids,
data,
basePath: referenceBasePath,
currentSort,
setSort,
total
})}
{pagination && React.cloneElement(pagination, {
page,
perPage,
setPage,
setPerPage,
total
})}
);
//Assuming the question example, the presentation of many-to-many relationship would be something like
const UserShow = ({...props}) => (
}>
)
//Used because is what I use in my project, not sure if works under or , but I think it work since I use it in other contexts
Я думаю, что реализация может быть улучшена и более совместима с React-Admin. В других ссылочных полях выборка данных хранится в состоянии реакции-избыточности, в этой реализации это не так. Отношение нигде не сохраняется, кроме компонента, который не позволяет приложению работать в автономном режиме, поскольку не может извлекать данные, даже упорядочение невозможно.
Вы могли сделать файлы символьной ссылкой на другой каталог на Вашей машине для примеров / общий каталог на том же уровне как / текущий и выпуски/.
Выезд capistrano управляет журналом / и/tmp каталогами.
Для будущей записи это - задача, я раньше делал это с общим каталогом:
task :link_shared_directories do
run "ln -s #{shared_path}/files #{release_path}/public/files"
end
after "deploy:update_code", :link_shared_directories